rubocop-rspec 1.43.0 → 2.0.0.pre

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: afae94ff975b0caf19f7aa7cad514296a15cb28afc2c4ae18dba8bb8446290d2
4
- data.tar.gz: 15fa2c9138a72d6c54247ea384793abbf9c3d6f8eaa2d7346803c8a296b7335c
3
+ metadata.gz: '0940bab64e9ccca8168ff22b5d0f46e6777d015316da687deca6072f0e8c25f0'
4
+ data.tar.gz: c7a518dbbcbcb63567b4491849a8ef79834051350614a8b8bf284f7f8a31f83d
5
5
  SHA512:
6
- metadata.gz: 4552fb307a57b49ed209ac12a30744b97cfd5bc57460a2a385a16a176903f439083a634e92a8f4faf308c3cca570a79bc8ba85b8a51ceff295920614f1bfe95b
7
- data.tar.gz: f6b9ed4c0d486fb41c477f6b9d95b7a8897b2011f80bc91edd1a872dca11fce0e5de05387f5e0a3112a0849f371fa93c1eba220019ff5b81ba9959daf06e089c
6
+ metadata.gz: 9f8b127bdccb77c1f85268adc1cd60f614423914f5889557e71b59a36b040e8dc8c42ad409b4310dd039d5621f928bac3f60293d07e6f419762c02ddfbd5ddb0
7
+ data.tar.gz: 23922bf8587a2ac38a1bf76b973ba64ec586cf0ea6d32f65602257fd9782ddc8d4553ff964f9ff833c423e19bdb59f733593ca91539ecab709e0b6b29bf3ae53
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.0.0.pre (2020-10-22)
6
+
7
+ * Update RuboCop dependency to v1.0.0. ([@bquorning][])
8
+ * **(Potentially breaking)** Change namespace of several cops (`Capybara/*` -> `RSpec/Capybara/*`, `FactoryBot/*` -> `RSpec/FactoryBot/*`, `Rails/*` -> `RSpec/Rails/*`). ([@pirj][], [@bquorning][])
9
+
10
+ ## 1.44.1 (2020-10-20)
11
+
12
+ * Relax `rubocop-ast` version constraint. ([@PhilCoggins][])
13
+
14
+ ## 1.44.0 (2020-10-20)
15
+
16
+ * Move our documentation from rubocop-rspec.readthedocs.io to docs.rubocop.org/rubocop-rspec. ([@bquorning][])
17
+ * Add `RSpec/RepeatedIncludeExample` cop. ([@biinari][])
18
+ * Add `RSpec/StubbedMock` cop. ([@bquorning][], [@pirj][])
19
+ * Add `IgnoredMetadata` configuration option to `RSpec/DescribeClass`. ([@Rafix02][])
20
+ * Fix false positives in `RSpec/EmptyExampleGroup`. ([@pirj][])
21
+ * Fix a false positive for `RSpec/EmptyExampleGroup` when example is defined in an `if` branch. ([@koic][])
22
+
23
+ ## 1.43.2 (2020-08-25)
24
+
25
+ * Fix `RSpec/FilePath` when checking a file with a shared example. ([@pirj][])
26
+ * Fix subject nesting detection in `RSpec/LeadingSubject`. ([@pirj][])
27
+
28
+ ## 1.43.1 (2020-08-17)
29
+
30
+ * Fix `RSpec/FilePath` when checking a file defining e.g. an empty class. ([@bquorning][])
31
+
5
32
  ## 1.43.0 (2020-08-17)
6
33
 
7
34
  * Add a new base cop class `::RuboCop::Cop::RSpec::Base`. The old base class `::RuboCop::Cop::RSpec::Cop` is deprecated, and will be removed in the next major release. ([@bquorning][])
@@ -497,8 +524,8 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
497
524
  [@rspeicher]: https://github.com/rspeicher
498
525
  [@jonatas]: https://github.com/jonatas
499
526
  [@pocke]: https://github.com/pocke
500
- [@bmorrall]: https:/github.com/bmorrall
501
- [@zverok]: https:/github.com/zverok
527
+ [@bmorrall]: https://github.com/bmorrall
528
+ [@zverok]: https://github.com/zverok
502
529
  [@timrogers]: https://github.com/timrogers
503
530
  [@yevhene]: https://github.com/yevhene
504
531
  [@walf443]: https://github.com/walf443
@@ -543,6 +570,9 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
543
570
  [@andrykonchin]: https://github.com/andrykonchin
544
571
  [@harrylewis]: https://github.com/harrylewis
545
572
  [@elliterate]: https://github.com/elliterate
546
- [@mlarraz]: https://github.com/mlarraz
547
573
  [@jtannas]: https://github.com/jtannas
548
574
  [@mockdeep]: https://github.com/mockdeep
575
+ [@biinari]: https://github.com/biinari
576
+ [@koic]: https://github.com/koic
577
+ [@Rafix02]: https://github.com/Rafix02
578
+ [@PhilCoggins]: https://github.com/PhilCoggins
@@ -76,7 +76,23 @@ RSpec/ContextWording:
76
76
  RSpec/DescribeClass:
77
77
  Description: Check that the first argument to the top-level describe is a constant.
78
78
  Enabled: true
79
+ IgnoredMetadata:
80
+ type:
81
+ - channel
82
+ - controller
83
+ - helper
84
+ - job
85
+ - mailer
86
+ - model
87
+ - request
88
+ - routing
89
+ - view
90
+ - feature
91
+ - system
92
+ - mailbox
93
+ - aruba
79
94
  VersionAdded: '1.0'
95
+ VersionChanged: '1.44'
80
96
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribeClass
81
97
 
82
98
  RSpec/DescribeMethod:
@@ -494,6 +510,12 @@ RSpec/RepeatedExampleGroupDescription:
494
510
  VersionAdded: '1.38'
495
511
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedExampleGroupDescription
496
512
 
513
+ RSpec/RepeatedIncludeExample:
514
+ Description: Check for repeated include of shared examples.
515
+ Enabled: true
516
+ VersionAdded: '1.44'
517
+ StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample
518
+
497
519
  RSpec/ReturnFromStub:
498
520
  Description: Checks for consistent style of stub's return setting.
499
521
  Enabled: true
@@ -537,6 +559,12 @@ RSpec/SingleArgumentMessageChain:
537
559
  VersionChanged: '1.10'
538
560
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SingleArgumentMessageChain
539
561
 
562
+ RSpec/StubbedMock:
563
+ Description: Checks that message expectations do not have a configured response.
564
+ Enabled: pending
565
+ VersionAdded: '1.44'
566
+ StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/StubbedMock
567
+
540
568
  RSpec/SubjectStub:
541
569
  Description: Checks for stubbed test subjects.
542
570
  Enabled: true
@@ -592,33 +620,36 @@ RSpec/Yield:
592
620
  VersionAdded: '1.32'
593
621
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Yield
594
622
 
595
- Capybara/CurrentPathExpectation:
623
+ RSpec/Capybara/CurrentPathExpectation:
596
624
  Description: Checks that no expectations are set on Capybara's `current_path`.
597
625
  Enabled: true
598
626
  VersionAdded: '1.18'
627
+ VersionChanged: '2.0'
599
628
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/CurrentPathExpectation
600
629
 
601
- Capybara/FeatureMethods:
630
+ RSpec/Capybara/FeatureMethods:
602
631
  Description: Checks for consistent method usage in feature specs.
603
632
  Enabled: true
604
633
  EnabledMethods: []
605
634
  VersionAdded: '1.17'
606
- VersionChanged: '1.25'
635
+ VersionChanged: '2.0'
607
636
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods
608
637
 
609
- Capybara/VisibilityMatcher:
638
+ RSpec/Capybara/VisibilityMatcher:
610
639
  Description: Checks for boolean visibility in capybara finders.
611
640
  Enabled: true
612
641
  VersionAdded: '1.39'
642
+ VersionChanged: '2.0'
613
643
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/VisibilityMatcher
614
644
 
615
- FactoryBot/AttributeDefinedStatically:
645
+ RSpec/FactoryBot/AttributeDefinedStatically:
616
646
  Description: Always declare attribute values as blocks.
617
647
  Enabled: true
618
648
  VersionAdded: '1.28'
649
+ VersionChanged: '2.0'
619
650
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/AttributeDefinedStatically
620
651
 
621
- FactoryBot/CreateList:
652
+ RSpec/FactoryBot/CreateList:
622
653
  Description: Checks for create_list usage.
623
654
  Enabled: true
624
655
  EnforcedStyle: create_list
@@ -626,15 +657,17 @@ FactoryBot/CreateList:
626
657
  - create_list
627
658
  - n_times
628
659
  VersionAdded: '1.25'
660
+ VersionChanged: '2.0'
629
661
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/CreateList
630
662
 
631
- FactoryBot/FactoryClassName:
663
+ RSpec/FactoryBot/FactoryClassName:
632
664
  Description: Use string value when setting the class attribute explicitly.
633
665
  Enabled: true
634
666
  VersionAdded: '1.37'
667
+ VersionChanged: '2.0'
635
668
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryBot/FactoryClassName
636
669
 
637
- Rails/HttpStatus:
670
+ RSpec/Rails/HttpStatus:
638
671
  Description: Enforces use of symbolic or numeric value to describe HTTP status.
639
672
  Enabled: true
640
673
  EnforcedStyle: symbolic
@@ -642,4 +675,5 @@ Rails/HttpStatus:
642
675
  - numeric
643
676
  - symbolic
644
677
  VersionAdded: '1.23'
678
+ VersionChanged: '2.0'
645
679
  StyleGuide: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus
@@ -31,7 +31,7 @@ module RuboCop
31
31
  'or `%<arg>s.run`.'
32
32
 
33
33
  def_node_matcher :hook, <<-PATTERN
34
- (block {(send nil? :around) (send nil? :around sym)} (args $...) ...)
34
+ (block (send nil? :around sym ?) (args $...) ...)
35
35
  PATTERN
36
36
 
37
37
  def_node_search :find_arg_usage, <<-PATTERN
@@ -29,6 +29,8 @@ module RuboCop
29
29
  .map(&Regexp.public_method(:new))
30
30
  )
31
31
 
32
+ exclude_from_registry
33
+
32
34
  # Invoke the original inherited hook so our cops are recognized
33
35
  def self.inherited(subclass) # rubocop:disable Lint/MissingSuper
34
36
  RuboCop::Cop::Base.inherited(subclass)
@@ -5,6 +5,19 @@ module RuboCop
5
5
  module RSpec
6
6
  # Check that the first argument to the top-level describe is a constant.
7
7
  #
8
+ # It can be configured to ignore strings when certain metadata is passed.
9
+ #
10
+ # Ignores Rails and Aruba `type` metadata by default.
11
+ #
12
+ # @example `IgnoredMetadata` configuration
13
+ #
14
+ # # .rubocop.yml
15
+ # # RSpec/DescribeClass:
16
+ # # IgnoredMetadata:
17
+ # # type:
18
+ # # - request
19
+ # # - controller
20
+ #
8
21
  # @example
9
22
  # # bad
10
23
  # describe 'Do something' do
@@ -27,36 +40,42 @@ module RuboCop
27
40
  MSG = 'The first argument to describe should be '\
28
41
  'the class or module being tested.'
29
42
 
30
- def_node_matcher :rails_metadata?, <<-PATTERN
31
- (pair
32
- (sym :type)
33
- (sym { :channel :controller :helper :job :mailer :model :request
34
- :routing :view :feature :system :mailbox })
35
- )
36
- PATTERN
37
-
38
- def_node_matcher :example_group_with_rails_metadata?, <<~PATTERN
39
- (send #rspec? :describe ... (hash <#rails_metadata? ...>))
43
+ def_node_matcher :example_group_with_ignored_metadata?, <<~PATTERN
44
+ (send #rspec? :describe ... (hash <#ignored_metadata? ...>))
40
45
  PATTERN
41
46
 
42
47
  def_node_matcher :not_a_const_described, <<~PATTERN
43
48
  (send #rspec? :describe $[!const !#string_constant?] ...)
44
49
  PATTERN
45
50
 
46
- def on_top_level_group(top_level_node)
47
- return if example_group_with_rails_metadata?(top_level_node.send_node)
51
+ def_node_matcher :sym_pair, <<~PATTERN
52
+ (pair $sym $sym)
53
+ PATTERN
48
54
 
49
- not_a_const_described(top_level_node.send_node) do |described|
55
+ def on_top_level_group(node)
56
+ return if example_group_with_ignored_metadata?(node.send_node)
57
+
58
+ not_a_const_described(node.send_node) do |described|
50
59
  add_offense(described)
51
60
  end
52
61
  end
53
62
 
54
63
  private
55
64
 
65
+ def ignored_metadata?(node)
66
+ sym_pair(node) do |key, value|
67
+ ignored_metadata[key.value.to_s].to_a.include?(value.value.to_s)
68
+ end
69
+ end
70
+
56
71
  def string_constant?(described)
57
72
  described.str_type? &&
58
73
  described.value.match?(/^(?:(?:::)?[A-Z]\w*)+$/)
59
74
  end
75
+
76
+ def ignored_metadata
77
+ cop_config['IgnoredMetadata'] || {}
78
+ end
60
79
  end
61
80
  end
62
81
  end
@@ -33,6 +33,11 @@ module RuboCop
33
33
  # end
34
34
  # end
35
35
  #
36
+ # # good
37
+ # describe Bacon do
38
+ # pending 'will add tests later'
39
+ # end
40
+ #
36
41
  # @example configuration
37
42
  #
38
43
  # # .rubocop.yml
@@ -83,11 +88,14 @@ module RuboCop
83
88
  # it_behaves_like 'an animal'
84
89
  # it_behaves_like('a cat') { let(:food) { 'milk' } }
85
90
  # it_has_root_access
91
+ # skip
92
+ # it 'will be implemented later'
86
93
  #
87
94
  # @param node [RuboCop::AST::Node]
88
95
  # @return [Array<RuboCop::AST::Node>] matching nodes
89
96
  def_node_matcher :example_or_group_or_include?, <<~PATTERN
90
97
  {
98
+ #{Examples::ALL.send_pattern}
91
99
  #{Examples::ALL.block_pattern}
92
100
  #{ExampleGroups::ALL.block_pattern}
93
101
  #{Includes::ALL.send_pattern}
@@ -152,13 +160,39 @@ module RuboCop
152
160
  PATTERN
153
161
 
154
162
  def on_block(node)
163
+ return if node.each_ancestor(:def, :defs).any?
164
+ return if node.each_ancestor(:block).any? { |block| example?(block) }
165
+
155
166
  example_group_body(node) do |body|
156
- add_offense(node.send_node) unless examples?(body)
167
+ add_offense(node.send_node) if offensive?(body)
157
168
  end
158
169
  end
159
170
 
160
171
  private
161
172
 
173
+ def offensive?(body)
174
+ return true unless body
175
+ return false if conditionals_with_examples?(body)
176
+
177
+ if body.if_type?
178
+ !examples_in_branches?(body)
179
+ else
180
+ !examples?(body)
181
+ end
182
+ end
183
+
184
+ def conditionals_with_examples?(body)
185
+ return unless body.begin_type?
186
+
187
+ body.each_descendant(:if).any? do |if_node|
188
+ examples_in_branches?(if_node)
189
+ end
190
+ end
191
+
192
+ def examples_in_branches?(if_node)
193
+ if_node.branches.any? { |branch| examples?(branch) }
194
+ end
195
+
162
196
  def custom_include?(method_name)
163
197
  custom_include_methods.include?(method_name)
164
198
  end
@@ -69,7 +69,7 @@ module RuboCop
69
69
 
70
70
  def_node_search :routing_metadata?, '(pair (sym :type) (sym :routing))'
71
71
 
72
- def on_top_level_group(node)
72
+ def on_top_level_example_group(node)
73
73
  return unless top_level_groups.one?
74
74
 
75
75
  const_described(node) do |send_node, described_class, arguments|
@@ -22,8 +22,7 @@ module RuboCop
22
22
  def_node_matcher :lambda?, <<-PATTERN
23
23
  {
24
24
  (send (const nil? :Proc) :new)
25
- (send nil? :proc)
26
- (send nil? :lambda)
25
+ (send nil? {:proc :lambda})
27
26
  }
28
27
  PATTERN
29
28
 
@@ -54,13 +54,17 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def offending_node(node)
57
- node.parent.each_child_node.find do |sibling|
57
+ parent(node).each_child_node.find do |sibling|
58
58
  break if sibling.equal?(node)
59
59
 
60
60
  yield sibling if offending?(sibling)
61
61
  end
62
62
  end
63
63
 
64
+ def parent(node)
65
+ node.each_ancestor(:block).first.body
66
+ end
67
+
64
68
  def autocorrect(corrector, node, sibling)
65
69
  RuboCop::RSpec::Corrector::MoveNode.new(
66
70
  node, corrector, processed_source
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check for repeated include of shared examples.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # describe 'foo' do
12
+ # include_examples 'cool stuff'
13
+ # include_examples 'cool stuff'
14
+ # end
15
+ #
16
+ # # bad
17
+ # describe 'foo' do
18
+ # it_behaves_like 'a cool', 'thing'
19
+ # it_behaves_like 'a cool', 'thing'
20
+ # end
21
+ #
22
+ # # bad
23
+ # context 'foo' do
24
+ # it_should_behave_like 'a duck'
25
+ # it_should_behave_like 'a duck'
26
+ # end
27
+ #
28
+ # # good
29
+ # describe 'foo' do
30
+ # include_examples 'cool stuff'
31
+ # end
32
+ #
33
+ # describe 'bar' do
34
+ # include_examples 'cool stuff'
35
+ # end
36
+ #
37
+ # # good
38
+ # describe 'foo' do
39
+ # it_behaves_like 'a cool', 'thing'
40
+ # it_behaves_like 'a cool', 'person'
41
+ # end
42
+ #
43
+ # # good
44
+ # context 'foo' do
45
+ # it_should_behave_like 'a duck'
46
+ # it_should_behave_like 'a goose'
47
+ # end
48
+ #
49
+ class RepeatedIncludeExample < Base
50
+ MSG = 'Repeated include of shared_examples %<name>s ' \
51
+ 'on line(s) %<repeat>s'
52
+
53
+ def_node_matcher :several_include_examples?, <<-PATTERN
54
+ (begin <#include_examples? #include_examples? ...>)
55
+ PATTERN
56
+
57
+ def_node_matcher :include_examples?, Includes::EXAMPLES.send_pattern
58
+
59
+ def_node_matcher :shared_examples_name, <<-PATTERN
60
+ (send _ #{Includes::EXAMPLES.node_pattern_union} $_ ...)
61
+ PATTERN
62
+
63
+ def on_begin(node)
64
+ return unless several_include_examples?(node)
65
+
66
+ repeated_include_examples(node).each do |item, repeats|
67
+ add_offense(item, message: message(item, repeats))
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def repeated_include_examples(node)
74
+ node
75
+ .children
76
+ .select { |child| literal_include_examples?(child) }
77
+ .group_by { |child| signature_keys(child) }
78
+ .values
79
+ .reject(&:one?)
80
+ .flat_map { |items| add_repeated_lines(items) }
81
+ end
82
+
83
+ def literal_include_examples?(node)
84
+ include_examples?(node) &&
85
+ node.arguments.all?(&:recursive_literal_or_const?)
86
+ end
87
+
88
+ def add_repeated_lines(items)
89
+ repeated_lines = items.map(&:first_line)
90
+ items.map { |item| [item, repeated_lines - [item.first_line]] }
91
+ end
92
+
93
+ def signature_keys(item)
94
+ item.arguments
95
+ end
96
+
97
+ def message(item, repeats)
98
+ format(MSG, name: shared_examples_name(item).source, repeat: repeats)
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Checks that message expectations do not have a configured response.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # expect(foo).to receive(:bar).with(42).and_return("hello world")
12
+ #
13
+ # # good (without spies)
14
+ # allow(foo).to receive(:bar).with(42).and_return("hello world")
15
+ # expect(foo).to receive(:bar).with(42)
16
+ #
17
+ class StubbedMock < Base
18
+ MSG = 'Prefer `%<replacement>s` over `%<method_name>s` when ' \
19
+ 'configuring a response.'
20
+
21
+ # @!method message_expectation?(node)
22
+ # Match message expectation matcher
23
+ #
24
+ # @example source that matches
25
+ # receive(:foo)
26
+ #
27
+ # @example source that matches
28
+ # receive_message_chain(:foo, :bar)
29
+ #
30
+ # @example source that matches
31
+ # receive(:foo).with('bar')
32
+ #
33
+ # @param node [RuboCop::AST::Node]
34
+ # @return [Array<RuboCop::AST::Node>] matching nodes
35
+ def_node_matcher :message_expectation?, <<-PATTERN
36
+ {
37
+ (send nil? { :receive :receive_message_chain } ...) # receive(:foo)
38
+ (send (send nil? :receive ...) :with ...) # receive(:foo).with('bar')
39
+ }
40
+ PATTERN
41
+
42
+ def_node_matcher :configured_response?, <<~PATTERN
43
+ { :and_return :and_raise :and_throw :and_yield
44
+ :and_call_original :and_wrap_original }
45
+ PATTERN
46
+
47
+ # @!method expectation(node)
48
+ # Match expectation
49
+ #
50
+ # @example source that matches
51
+ # is_expected.to be_in_the_bar
52
+ #
53
+ # @example source that matches
54
+ # expect(cocktail).to contain_exactly(:fresh_orange_juice, :campari)
55
+ #
56
+ # @example source that matches
57
+ # expect_any_instance_of(Officer).to be_alert
58
+ #
59
+ # @param node [RuboCop::AST::Node]
60
+ # @yield [RuboCop::AST::Node] expectation, method name, matcher
61
+ def_node_matcher :expectation, <<~PATTERN
62
+ (send
63
+ $(send nil? $#{Expectations::ALL.node_pattern_union} ...)
64
+ :to $_)
65
+ PATTERN
66
+
67
+ # @!method matcher_with_configured_response(node)
68
+ # Match matcher with a configured response
69
+ #
70
+ # @example source that matches
71
+ # receive(:foo).and_return('bar')
72
+ #
73
+ # @example source that matches
74
+ # receive(:lower).and_raise(SomeError)
75
+ #
76
+ # @example source that matches
77
+ # receive(:redirect).and_call_original
78
+ #
79
+ # @param node [RuboCop::AST::Node]
80
+ # @yield [RuboCop::AST::Node] matcher
81
+ def_node_matcher :matcher_with_configured_response, <<~PATTERN
82
+ (send #message_expectation? #configured_response? _)
83
+ PATTERN
84
+
85
+ # @!method matcher_with_return_block(node)
86
+ # Match matcher with a return block
87
+ #
88
+ # @example source that matches
89
+ # receive(:foo) { 'bar' }
90
+ #
91
+ # @param node [RuboCop::AST::Node]
92
+ # @yield [RuboCop::AST::Node] matcher
93
+ def_node_matcher :matcher_with_return_block, <<~PATTERN
94
+ (block #message_expectation? args _) # receive(:foo) { 'bar' }
95
+ PATTERN
96
+
97
+ # @!method matcher_with_hash(node)
98
+ # Match matcher with a configured response defined as a hash
99
+ #
100
+ # @example source that matches
101
+ # receive_messages(foo: 'bar', baz: 'qux')
102
+ #
103
+ # @example source that matches
104
+ # receive_message_chain(:foo, bar: 'baz')
105
+ #
106
+ # @param node [RuboCop::AST::Node]
107
+ # @yield [RuboCop::AST::Node] matcher
108
+ def_node_matcher :matcher_with_hash, <<~PATTERN
109
+ {
110
+ (send nil? :receive_messages hash) # receive_messages(foo: 'bar', baz: 'qux')
111
+ (send nil? :receive_message_chain ... hash) # receive_message_chain(:foo, bar: 'baz')
112
+ }
113
+ PATTERN
114
+
115
+ # @!method matcher_with_blockpass(node)
116
+ # Match matcher with a configured response in block-pass
117
+ #
118
+ # @example source that matches
119
+ # receive(:foo, &canned)
120
+ #
121
+ # @example source that matches
122
+ # receive_message_chain(:foo, :bar, &canned)
123
+ #
124
+ # @example source that matches
125
+ # receive(:foo).with('bar', &canned)
126
+ #
127
+ # @param node [RuboCop::AST::Node]
128
+ # @yield [RuboCop::AST::Node] matcher
129
+ def_node_matcher :matcher_with_blockpass, <<~PATTERN
130
+ {
131
+ (send nil? { :receive :receive_message_chain } ... block_pass) # receive(:foo, &canned)
132
+ (send (send nil? :receive ...) :with ... block_pass) # receive(:foo).with('foo', &canned)
133
+ }
134
+ PATTERN
135
+
136
+ def on_send(node)
137
+ expectation(node, &method(:on_expectation))
138
+ end
139
+
140
+ private
141
+
142
+ def on_expectation(expectation, method_name, matcher)
143
+ flag_expectation = lambda do
144
+ add_offense(expectation, message: msg(method_name))
145
+ end
146
+
147
+ matcher_with_configured_response(matcher, &flag_expectation)
148
+ matcher_with_return_block(matcher, &flag_expectation)
149
+ matcher_with_hash(matcher, &flag_expectation)
150
+ matcher_with_blockpass(matcher, &flag_expectation)
151
+ end
152
+
153
+ def msg(method_name)
154
+ format(MSG,
155
+ method_name: method_name,
156
+ replacement: replacement(method_name))
157
+ end
158
+
159
+ def replacement(method_name)
160
+ case method_name
161
+ when :expect
162
+ :allow
163
+ when :is_expected
164
+ 'allow(subject)'
165
+ when :expect_any_instance_of
166
+ :allow_any_instance_of
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -43,10 +43,10 @@ module RuboCop
43
43
  #
44
44
  # @yield [Symbol] subject name
45
45
  def_node_matcher :subject, <<-PATTERN
46
- {
47
- (block (send nil? :subject (sym $_)) args ...)
48
- (block (send nil? $:subject) args ...)
49
- }
46
+ (block
47
+ (send nil?
48
+ {:subject (sym $_) | $:subject}
49
+ ) args ...)
50
50
  PATTERN
51
51
 
52
52
  # @!method message_expectation?(node, method_name)
@@ -79,12 +79,14 @@ require_relative 'rspec/repeated_description'
79
79
  require_relative 'rspec/repeated_example'
80
80
  require_relative 'rspec/repeated_example_group_body'
81
81
  require_relative 'rspec/repeated_example_group_description'
82
+ require_relative 'rspec/repeated_include_example'
82
83
  require_relative 'rspec/return_from_stub'
83
84
  require_relative 'rspec/scattered_let'
84
85
  require_relative 'rspec/scattered_setup'
85
86
  require_relative 'rspec/shared_context'
86
87
  require_relative 'rspec/shared_examples'
87
88
  require_relative 'rspec/single_argument_message_chain'
89
+ require_relative 'rspec/stubbed_mock'
88
90
  require_relative 'rspec/subject_stub'
89
91
  require_relative 'rspec/unspecified_exception'
90
92
  require_relative 'rspec/variable_definition'
@@ -6,11 +6,7 @@ module RuboCop
6
6
  class Hook < Concept
7
7
  def_node_matcher :extract_metadata, <<~PATTERN
8
8
  (block
9
- {
10
- (send _ _ #valid_scope? $...)
11
- (send _ _ $...)
12
- }
13
- ...
9
+ (send _ _ #valid_scope? ? $...) ...
14
10
  )
15
11
  PATTERN
16
12
 
@@ -7,7 +7,8 @@ module RuboCop
7
7
  # Set of method selectors
8
8
  class SelectorSet
9
9
  def initialize(selectors)
10
- @selectors = selectors
10
+ @selectors = selectors.freeze
11
+ freeze
11
12
  end
12
13
 
13
14
  def ==(other)
@@ -50,6 +51,10 @@ module RuboCop
50
51
  selectors.map(&:inspect).join(' ')
51
52
  end
52
53
 
54
+ def to_a
55
+ selectors
56
+ end
57
+
53
58
  protected
54
59
 
55
60
  attr_reader :selectors
@@ -16,7 +16,7 @@ module RuboCop
16
16
  return unless root_node
17
17
 
18
18
  top_level_groups.each do |node|
19
- example_group?(node, &method(:on_top_level_example_group))
19
+ on_top_level_example_group(node) if example_group?(node)
20
20
  on_top_level_group(node)
21
21
  end
22
22
  end
@@ -29,16 +29,18 @@ module RuboCop
29
29
  private
30
30
 
31
31
  # Dummy methods to be overridden in the consumer
32
- def on_top_level_example_group; end
32
+ def on_top_level_example_group(_node); end
33
33
 
34
- def on_top_level_group; end
34
+ def on_top_level_group(_node); end
35
35
 
36
36
  def top_level_group?(node)
37
37
  top_level_groups.include?(node)
38
38
  end
39
39
 
40
40
  def top_level_nodes(node)
41
- if node.begin_type?
41
+ if node.nil?
42
+ []
43
+ elsif node.begin_type?
42
44
  node.children
43
45
  elsif node.module_type? || node.class_type?
44
46
  top_level_nodes(node.body)
@@ -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.43.0'
7
+ STRING = '2.0.0.pre'
8
8
  end
9
9
  end
10
10
  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.43.0
4
+ version: 2.0.0.pre
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: 2020-08-17 00:00:00.000000000 Z
13
+ date: 2020-10-22 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -18,14 +18,14 @@ dependencies:
18
18
  requirements:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: '0.87'
21
+ version: '1.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: '0.87'
28
+ version: '1.0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rack
31
31
  requirement: !ruby/object:Gem::Requirement
@@ -203,12 +203,14 @@ files:
203
203
  - lib/rubocop/cop/rspec/repeated_example.rb
204
204
  - lib/rubocop/cop/rspec/repeated_example_group_body.rb
205
205
  - lib/rubocop/cop/rspec/repeated_example_group_description.rb
206
+ - lib/rubocop/cop/rspec/repeated_include_example.rb
206
207
  - lib/rubocop/cop/rspec/return_from_stub.rb
207
208
  - lib/rubocop/cop/rspec/scattered_let.rb
208
209
  - lib/rubocop/cop/rspec/scattered_setup.rb
209
210
  - lib/rubocop/cop/rspec/shared_context.rb
210
211
  - lib/rubocop/cop/rspec/shared_examples.rb
211
212
  - lib/rubocop/cop/rspec/single_argument_message_chain.rb
213
+ - lib/rubocop/cop/rspec/stubbed_mock.rb
212
214
  - lib/rubocop/cop/rspec/subject_stub.rb
213
215
  - lib/rubocop/cop/rspec/unspecified_exception.rb
214
216
  - lib/rubocop/cop/rspec/variable_definition.rb
@@ -243,7 +245,7 @@ licenses:
243
245
  - MIT
244
246
  metadata:
245
247
  changelog_uri: https://github.com/rubocop-hq/rubocop-rspec/blob/master/CHANGELOG.md
246
- documentation_uri: https://rubocop-rspec.readthedocs.io/
248
+ documentation_uri: https://docs.rubocop.org/rubocop-rspec/
247
249
  post_install_message:
248
250
  rdoc_options: []
249
251
  require_paths:
@@ -255,9 +257,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
255
257
  version: 2.4.0
256
258
  required_rubygems_version: !ruby/object:Gem::Requirement
257
259
  requirements:
258
- - - ">="
260
+ - - ">"
259
261
  - !ruby/object:Gem::Version
260
- version: '0'
262
+ version: 1.3.1
261
263
  requirements: []
262
264
  rubygems_version: 3.0.3
263
265
  signing_key: