rubocop-rspec 2.26.1 → 2.27.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 +14 -0
 - data/config/default.yml +18 -1
 - data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
 - data/lib/rubocop/cop/rspec/change_by_zero.rb +27 -1
 - data/lib/rubocop/cop/rspec/described_class.rb +3 -2
 - data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
 - data/lib/rubocop/cop/rspec/expect_actual.rb +5 -2
 - data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
 - data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
 - data/lib/rubocop/cop/rspec/message_expectation.rb +0 -1
 - data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
 - data/lib/rubocop/cop/rspec/multiple_expectations.rb +10 -5
 - data/lib/rubocop/cop/rspec/rails/have_http_status.rb +33 -9
 - data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +314 -22
 - data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +4 -2
 - data/lib/rubocop/cop/rspec/repeated_subject_call.rb +120 -0
 - data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -2
 - data/lib/rubocop/cop/rspec_cops.rb +2 -0
 - data/lib/rubocop/rspec/version.rb +1 -1
 - metadata +5 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 66ac0fecdd858c4b85a06c9e32bdb680d2dd92aa98100740302702a1737a3a9c
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 5f688cf15390149d3a7dc6e2d3f67e041248ee43a3dce5e47d2370d2813f86d7
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 9fc186d4d8191c461dee977e64a2f0a295389be99a8054bb7ba75258e429c5c3dc624b3d6da0a19b65a024648f1922ef9646af5bac44d0b7c3255a99ac8e14bc
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 1d603f51d43de587cd17939252604f1d86d371e0e6883a4289c219eed97d0862bd4ba29d2c3ba205ceed3c4157692d4fbbf52b63d020facfd0d2798bef284e55
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -2,6 +2,18 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            ## Master (Unreleased)
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            ## 2.27.0 (2024-03-01)
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - Add new `RSpec/IsExpectedSpecify` cop. ([@ydah])
         
     | 
| 
      
 8 
     | 
    
         
            +
            - Add new `RSpec/RepeatedSubjectCall` cop. ([@drcapulet])
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Add support for `assert_true`, `assert_false`, `assert_not_equal`, `assert_not_nil`, `*_empty`, `*_predicate`, `*_kind_of`, `*_in_delta`, `*_match`, `*_instance_of` and `*_includes` assertions in `RSpec/Rails/MinitestAssertions`. ([@ydah], [@G-Rath])
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Support asserts with messages in `Rspec/BeEmpty`. ([@G-Rath])
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Fix a false positive for `RSpec/ExpectActual` when used with rspec-rails routing matchers. ([@naveg])
         
     | 
| 
      
 12 
     | 
    
         
            +
            - Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Fix a false negative for `RSpec/DescribedClass` when class with constant. ([@ydah])
         
     | 
| 
      
 14 
     | 
    
         
            +
            - Fix a false positive for `RSpec/ExampleWithoutDescription` when `specify` with multi-line block and missing description. ([@ydah])
         
     | 
| 
      
 15 
     | 
    
         
            +
            - Fix an incorrect autocorrect for `RSpec/ChangeByZero` when compound expectations with line break before `.by(0)`. ([@ydah])
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
       5 
17 
     | 
    
         
             
            ## 2.26.1 (2024-01-05)
         
     | 
| 
       6 
18 
     | 
    
         | 
| 
       7 
19 
     | 
    
         
             
            - Fix an error for `RSpec/SharedExamples` when using examples without argument. ([@ydah])
         
     | 
| 
         @@ -845,6 +857,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. 
     | 
|
| 
       845 
857 
     | 
    
         
             
            [@deivid-rodriguez]: https://github.com/deivid-rodriguez
         
     | 
| 
       846 
858 
     | 
    
         
             
            [@dgollahon]: https://github.com/dgollahon
         
     | 
| 
       847 
859 
     | 
    
         
             
            [@dmitrytsepelev]: https://github.com/dmitrytsepelev
         
     | 
| 
      
 860 
     | 
    
         
            +
            [@drcapulet]: https://github.com/drcapulet
         
     | 
| 
       848 
861 
     | 
    
         
             
            [@drowze]: https://github.com/Drowze
         
     | 
| 
       849 
862 
     | 
    
         
             
            [@dswij]: https://github.com/dswij
         
     | 
| 
       850 
863 
     | 
    
         
             
            [@dvandersluis]: https://github.com/dvandersluis
         
     | 
| 
         @@ -892,6 +905,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features. 
     | 
|
| 
       892 
905 
     | 
    
         
             
            [@mockdeep]: https://github.com/mockdeep
         
     | 
| 
       893 
906 
     | 
    
         
             
            [@mothonmars]: https://github.com/MothOnMars
         
     | 
| 
       894 
907 
     | 
    
         
             
            [@mvz]: https://github.com/mvz
         
     | 
| 
      
 908 
     | 
    
         
            +
            [@naveg]: https://github.com/naveg
         
     | 
| 
       895 
909 
     | 
    
         
             
            [@nc-holodakg]: https://github.com/nc-holodakg
         
     | 
| 
       896 
910 
     | 
    
         
             
            [@nevir]: https://github.com/nevir
         
     | 
| 
       897 
911 
     | 
    
         
             
            [@ngouy]: https://github.com/ngouy
         
     | 
    
        data/config/default.yml
    CHANGED
    
    | 
         @@ -548,6 +548,13 @@ RSpec/InstanceVariable: 
     | 
|
| 
       548 
548 
     | 
    
         
             
              StyleGuide: https://rspec.rubystyle.guide/#instance-variables
         
     | 
| 
       549 
549 
     | 
    
         
             
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable
         
     | 
| 
       550 
550 
     | 
    
         | 
| 
      
 551 
     | 
    
         
            +
            RSpec/IsExpectedSpecify:
         
     | 
| 
      
 552 
     | 
    
         
            +
              Description: Check for `specify` with `is_expected` and one-liner expectations.
         
     | 
| 
      
 553 
     | 
    
         
            +
              Enabled: pending
         
     | 
| 
      
 554 
     | 
    
         
            +
              VersionAdded: '2.27'
         
     | 
| 
      
 555 
     | 
    
         
            +
              StyleGuide: https://rspec.rubystyle.guide/#it-and-specify
         
     | 
| 
      
 556 
     | 
    
         
            +
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify
         
     | 
| 
      
 557 
     | 
    
         
            +
             
     | 
| 
       551 
558 
     | 
    
         
             
            RSpec/ItBehavesLike:
         
     | 
| 
       552 
559 
     | 
    
         
             
              Description: Checks that only one `it_behaves_like` style is used.
         
     | 
| 
       553 
560 
     | 
    
         
             
              Enabled: true
         
     | 
| 
         @@ -813,6 +820,12 @@ RSpec/RepeatedIncludeExample: 
     | 
|
| 
       813 
820 
     | 
    
         
             
              VersionAdded: '1.44'
         
     | 
| 
       814 
821 
     | 
    
         
             
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample
         
     | 
| 
       815 
822 
     | 
    
         | 
| 
      
 823 
     | 
    
         
            +
            RSpec/RepeatedSubjectCall:
         
     | 
| 
      
 824 
     | 
    
         
            +
              Description: Checks for repeated calls to subject missing that it is memoized.
         
     | 
| 
      
 825 
     | 
    
         
            +
              Enabled: pending
         
     | 
| 
      
 826 
     | 
    
         
            +
              VersionAdded: '2.27'
         
     | 
| 
      
 827 
     | 
    
         
            +
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedSubjectCall
         
     | 
| 
      
 828 
     | 
    
         
            +
             
     | 
| 
       816 
829 
     | 
    
         
             
            RSpec/ReturnFromStub:
         
     | 
| 
       817 
830 
     | 
    
         
             
              Description: Checks for consistent style of stub's return setting.
         
     | 
| 
       818 
831 
     | 
    
         
             
              Enabled: true
         
     | 
| 
         @@ -1126,8 +1139,12 @@ RSpec/Rails/AvoidSetupHook: 
     | 
|
| 
       1126 
1139 
     | 
    
         
             
            RSpec/Rails/HaveHttpStatus:
         
     | 
| 
       1127 
1140 
     | 
    
         
             
              Description: Checks that tests use `have_http_status` instead of equality matchers.
         
     | 
| 
       1128 
1141 
     | 
    
         
             
              Enabled: pending
         
     | 
| 
      
 1142 
     | 
    
         
            +
              ResponseMethods:
         
     | 
| 
      
 1143 
     | 
    
         
            +
                - response
         
     | 
| 
      
 1144 
     | 
    
         
            +
                - last_response
         
     | 
| 
       1129 
1145 
     | 
    
         
             
              SafeAutoCorrect: false
         
     | 
| 
       1130 
1146 
     | 
    
         
             
              VersionAdded: '2.12'
         
     | 
| 
      
 1147 
     | 
    
         
            +
              VersionChanged: '2.27'
         
     | 
| 
       1131 
1148 
     | 
    
         
             
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
         
     | 
| 
       1132 
1149 
     | 
    
         | 
| 
       1133 
1150 
     | 
    
         
             
            RSpec/Rails/HttpStatus:
         
     | 
| 
         @@ -1166,7 +1183,7 @@ RSpec/Rails/InferredSpecType: 
     | 
|
| 
       1166 
1183 
     | 
    
         
             
                views: view
         
     | 
| 
       1167 
1184 
     | 
    
         | 
| 
       1168 
1185 
     | 
    
         
             
            RSpec/Rails/MinitestAssertions:
         
     | 
| 
       1169 
     | 
    
         
            -
              Description: Check if using Minitest matchers.
         
     | 
| 
      
 1186 
     | 
    
         
            +
              Description: Check if using Minitest-like matchers.
         
     | 
| 
       1170 
1187 
     | 
    
         
             
              Enabled: pending
         
     | 
| 
       1171 
1188 
     | 
    
         
             
              VersionAdded: '2.17'
         
     | 
| 
       1172 
1189 
     | 
    
         
             
              Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/MinitestAssertions
         
     | 
| 
         @@ -59,6 +59,8 @@ module RuboCop 
     | 
|
| 
       59 
59 
     | 
    
         
             
                  #
         
     | 
| 
       60 
60 
     | 
    
         
             
                  class ChangeByZero < Base
         
     | 
| 
       61 
61 
     | 
    
         
             
                    extend AutoCorrector
         
     | 
| 
      
 62 
     | 
    
         
            +
                    include RangeHelp
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
       62 
64 
     | 
    
         
             
                    MSG = 'Prefer `not_to change` over `to %<method>s.by(0)`.'
         
     | 
| 
       63 
65 
     | 
    
         
             
                    MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
         
     | 
| 
       64 
66 
     | 
    
         
             
                                   'over `%<method>s.by(0)`.'
         
     | 
| 
         @@ -140,8 +142,32 @@ module RuboCop 
     | 
|
| 
       140 
142 
     | 
    
         | 
| 
       141 
143 
     | 
    
         
             
                      change_nodes(node) do |change_node|
         
     | 
| 
       142 
144 
     | 
    
         
             
                        corrector.replace(change_node.loc.selector, negated_matcher)
         
     | 
| 
       143 
     | 
    
         
            -
                         
     | 
| 
      
 145 
     | 
    
         
            +
                        insert_operator(corrector, node, change_node)
         
     | 
| 
      
 146 
     | 
    
         
            +
                        remove_by_zero(corrector, node, change_node)
         
     | 
| 
      
 147 
     | 
    
         
            +
                      end
         
     | 
| 
      
 148 
     | 
    
         
            +
                    end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                    def insert_operator(corrector, node, change_node)
         
     | 
| 
      
 151 
     | 
    
         
            +
                      operator = node.right_siblings.first
         
     | 
| 
      
 152 
     | 
    
         
            +
                      return unless %i[& |].include?(operator)
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                      corrector.insert_after(
         
     | 
| 
      
 155 
     | 
    
         
            +
                        replace_node(node, change_node), " #{operator}"
         
     | 
| 
      
 156 
     | 
    
         
            +
                      )
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                    def replace_node(node, change_node)
         
     | 
| 
      
 160 
     | 
    
         
            +
                      expect_change_with_arguments(node) ? change_node : change_node.parent
         
     | 
| 
      
 161 
     | 
    
         
            +
                    end
         
     | 
| 
      
 162 
     | 
    
         
            +
             
     | 
| 
      
 163 
     | 
    
         
            +
                    def remove_by_zero(corrector, node, change_node)
         
     | 
| 
      
 164 
     | 
    
         
            +
                      range = node.loc.dot.with(end_pos: node.source_range.end_pos)
         
     | 
| 
      
 165 
     | 
    
         
            +
                      if change_node.loc.line == range.line
         
     | 
| 
       144 
166 
     | 
    
         
             
                        corrector.remove(range)
         
     | 
| 
      
 167 
     | 
    
         
            +
                      else
         
     | 
| 
      
 168 
     | 
    
         
            +
                        corrector.remove(
         
     | 
| 
      
 169 
     | 
    
         
            +
                          range_by_whole_lines(range, include_final_newline: true)
         
     | 
| 
      
 170 
     | 
    
         
            +
                        )
         
     | 
| 
       145 
171 
     | 
    
         
             
                      end
         
     | 
| 
       146 
172 
     | 
    
         
             
                    end
         
     | 
| 
       147 
173 
     | 
    
         | 
| 
         @@ -113,7 +113,7 @@ module RuboCop 
     | 
|
| 
       113 
113 
     | 
    
         
             
                    def find_usage(node, &block)
         
     | 
| 
       114 
114 
     | 
    
         
             
                      yield(node) if offensive?(node)
         
     | 
| 
       115 
115 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                      return if scope_change?(node) 
     | 
| 
      
 116 
     | 
    
         
            +
                      return if scope_change?(node)
         
     | 
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
                      node.each_child_node do |child|
         
     | 
| 
       119 
119 
     | 
    
         
             
                        find_usage(child, &block)
         
     | 
| 
         @@ -194,7 +194,8 @@ module RuboCop 
     | 
|
| 
       194 
194 
     | 
    
         
             
                    #   const_name(s(:const, s(:const, nil, :M), :C)) # => [:M, :C]
         
     | 
| 
       195 
195 
     | 
    
         
             
                    #   const_name(s(:const, s(:cbase), :C))          # => [nil, :C]
         
     | 
| 
       196 
196 
     | 
    
         
             
                    def const_name(node)
         
     | 
| 
       197 
     | 
    
         
            -
                      namespace 
     | 
| 
      
 197 
     | 
    
         
            +
                      namespace = node.namespace
         
     | 
| 
      
 198 
     | 
    
         
            +
                      name = node.short_name
         
     | 
| 
       198 
199 
     | 
    
         
             
                      if !namespace
         
     | 
| 
       199 
200 
     | 
    
         
             
                        [name]
         
     | 
| 
       200 
201 
     | 
    
         
             
                      elsif namespace.const_type?
         
     | 
| 
         @@ -7,6 +7,7 @@ module RuboCop 
     | 
|
| 
       7 
7 
     | 
    
         
             
                  #
         
     | 
| 
       8 
8 
     | 
    
         
             
                  # RSpec allows for auto-generated example descriptions when there is no
         
     | 
| 
       9 
9 
     | 
    
         
             
                  # description provided or the description is an empty one.
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # It is acceptable to use `specify` without a description
         
     | 
| 
       10 
11 
     | 
    
         
             
                  #
         
     | 
| 
       11 
12 
     | 
    
         
             
                  # This cop removes empty descriptions.
         
     | 
| 
       12 
13 
     | 
    
         
             
                  # It also defines whether auto-generated description is allowed, based
         
     | 
| 
         @@ -14,17 +15,24 @@ module RuboCop 
     | 
|
| 
       14 
15 
     | 
    
         
             
                  #
         
     | 
| 
       15 
16 
     | 
    
         
             
                  # This cop can be configured using the `EnforcedStyle` option
         
     | 
| 
       16 
17 
     | 
    
         
             
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #   # always good
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   specify do
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     result = service.call
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #     expect(result).to be(true)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
       17 
25 
     | 
    
         
             
                  # @example `EnforcedStyle: always_allow` (default)
         
     | 
| 
       18 
26 
     | 
    
         
             
                  #   # bad
         
     | 
| 
       19 
27 
     | 
    
         
             
                  #   it('') { is_expected.to be_good }
         
     | 
| 
       20 
     | 
    
         
            -
                  #    
     | 
| 
      
 28 
     | 
    
         
            +
                  #   specify '' do
         
     | 
| 
       21 
29 
     | 
    
         
             
                  #     result = service.call
         
     | 
| 
       22 
30 
     | 
    
         
             
                  #     expect(result).to be(true)
         
     | 
| 
       23 
31 
     | 
    
         
             
                  #   end
         
     | 
| 
       24 
32 
     | 
    
         
             
                  #
         
     | 
| 
       25 
33 
     | 
    
         
             
                  #   # good
         
     | 
| 
       26 
34 
     | 
    
         
             
                  #   it { is_expected.to be_good }
         
     | 
| 
       27 
     | 
    
         
            -
                  #    
     | 
| 
      
 35 
     | 
    
         
            +
                  #   specify do
         
     | 
| 
       28 
36 
     | 
    
         
             
                  #     result = service.call
         
     | 
| 
       29 
37 
     | 
    
         
             
                  #     expect(result).to be(true)
         
     | 
| 
       30 
38 
     | 
    
         
             
                  #   end
         
     | 
| 
         @@ -75,6 +83,7 @@ module RuboCop 
     | 
|
| 
       75 
83 
     | 
    
         
             
                    def check_example_without_description(node)
         
     | 
| 
       76 
84 
     | 
    
         
             
                      return if node.arguments?
         
     | 
| 
       77 
85 
     | 
    
         
             
                      return unless disallow_empty_description?(node)
         
     | 
| 
      
 86 
     | 
    
         
            +
                      return if node.method?(:specify) && node.parent.multiline?
         
     | 
| 
       78 
87 
     | 
    
         | 
| 
       79 
88 
     | 
    
         
             
                      add_offense(node, message: MSG_ADD_DESCRIPTION)
         
     | 
| 
       80 
89 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -50,7 +50,8 @@ module RuboCop 
     | 
|
| 
       50 
50 
     | 
    
         
             
                      regexp
         
     | 
| 
       51 
51 
     | 
    
         
             
                    ].freeze
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                     
     | 
| 
      
 53 
     | 
    
         
            +
                    SKIPPED_MATCHERS = %i[route_to be_routable].freeze
         
     | 
| 
      
 54 
     | 
    
         
            +
                    CORRECTABLE_MATCHERS = %i[eq eql equal be].freeze
         
     | 
| 
       54 
55 
     | 
    
         | 
| 
       55 
56 
     | 
    
         
             
                    # @!method expect_literal(node)
         
     | 
| 
       56 
57 
     | 
    
         
             
                    def_node_matcher :expect_literal, <<~PATTERN
         
     | 
| 
         @@ -66,8 +67,10 @@ module RuboCop 
     | 
|
| 
       66 
67 
     | 
    
         | 
| 
       67 
68 
     | 
    
         
             
                    def on_send(node)
         
     | 
| 
       68 
69 
     | 
    
         
             
                      expect_literal(node) do |actual, matcher, expected|
         
     | 
| 
      
 70 
     | 
    
         
            +
                        next if SKIPPED_MATCHERS.include?(matcher)
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
       69 
72 
     | 
    
         
             
                        add_offense(actual.source_range) do |corrector|
         
     | 
| 
       70 
     | 
    
         
            -
                          next unless  
     | 
| 
      
 73 
     | 
    
         
            +
                          next unless CORRECTABLE_MATCHERS.include?(matcher)
         
     | 
| 
       71 
74 
     | 
    
         
             
                          next if literal?(expected)
         
     | 
| 
       72 
75 
     | 
    
         | 
| 
       73 
76 
     | 
    
         
             
                          swap(corrector, actual, expected)
         
     | 
| 
         @@ -22,10 +22,7 @@ module RuboCop 
     | 
|
| 
       22 
22 
     | 
    
         
             
                    def on_gvasgn(node)
         
     | 
| 
       23 
23 
     | 
    
         
             
                      return unless inside_example_scope?(node)
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                       
     | 
| 
       26 
     | 
    
         
            -
                      variable_name, _rhs = *node
         
     | 
| 
       27 
     | 
    
         
            -
                      # rubocop:enable InternalAffairs/NodeDestructuring
         
     | 
| 
       28 
     | 
    
         
            -
                      name = variable_name[1..]
         
     | 
| 
      
 25 
     | 
    
         
            +
                      name = node.name[1..]
         
     | 
| 
       29 
26 
     | 
    
         
             
                      return unless name.eql?('stdout') || name.eql?('stderr')
         
     | 
| 
       30 
27 
     | 
    
         | 
| 
       31 
28 
     | 
    
         
             
                      add_offense(node.loc.name, message: format(MSG, name: name))
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module RSpec
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Check for `specify` with `is_expected` and one-liner expectations.
         
     | 
| 
      
 7 
     | 
    
         
            +
                  #
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #   specify { is_expected.to be_truthy }
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #   it { is_expected.to be_truthy }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #   specify do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #     # ...
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #   specify { expect(sqrt(4)).to eq(2) }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  class IsExpectedSpecify < Base
         
     | 
| 
      
 22 
     | 
    
         
            +
                    extend AutoCorrector
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    RESTRICT_ON_SEND = %i[specify].freeze
         
     | 
| 
      
 25 
     | 
    
         
            +
                    IS_EXPECTED_METHODS = ::Set[:is_expected, :are_expected].freeze
         
     | 
| 
      
 26 
     | 
    
         
            +
                    MSG = 'Use `it` instead of `specify`.'
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    # @!method offense?(node)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    def_node_matcher :offense?, <<~PATTERN
         
     | 
| 
      
 30 
     | 
    
         
            +
                      (block (send _ :specify) _ (send (send _ IS_EXPECTED_METHODS) ...))
         
     | 
| 
      
 31 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    def on_send(node)
         
     | 
| 
      
 34 
     | 
    
         
            +
                      block_node = node.parent
         
     | 
| 
      
 35 
     | 
    
         
            +
                      return unless block_node&.single_line? && offense?(block_node)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      selector = node.loc.selector
         
     | 
| 
      
 38 
     | 
    
         
            +
                      add_offense(selector) do |corrector|
         
     | 
| 
      
 39 
     | 
    
         
            +
                        corrector.replace(selector, 'it')
         
     | 
| 
      
 40 
     | 
    
         
            +
                      end
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -48,12 +48,17 @@ module RuboCop 
     | 
|
| 
       48 
48 
     | 
    
         
             
                  #     end
         
     | 
| 
       49 
49 
     | 
    
         
             
                  #   end
         
     | 
| 
       50 
50 
     | 
    
         
             
                  #
         
     | 
| 
       51 
     | 
    
         
            -
                  # @example  
     | 
| 
       52 
     | 
    
         
            -
                  #   #  
     | 
| 
       53 
     | 
    
         
            -
                  #    
     | 
| 
       54 
     | 
    
         
            -
                  # 
     | 
| 
      
 51 
     | 
    
         
            +
                  # @example `Max: 1` (default)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 53 
     | 
    
         
            +
                  #   describe UserCreator do
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #     it 'builds a user' do
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #       expect(user.name).to eq("John")
         
     | 
| 
      
 56 
     | 
    
         
            +
                  #       expect(user.age).to eq(22)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #   end
         
     | 
| 
       55 
59 
     | 
    
         
             
                  #
         
     | 
| 
       56 
     | 
    
         
            -
                  # 
     | 
| 
      
 60 
     | 
    
         
            +
                  # @example `Max: 2`
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #   # good
         
     | 
| 
       57 
62 
     | 
    
         
             
                  #   describe UserCreator do
         
     | 
| 
       58 
63 
     | 
    
         
             
                  #     it 'builds a user' do
         
     | 
| 
       59 
64 
     | 
    
         
             
                  #       expect(user.name).to eq("John")
         
     | 
| 
         @@ -6,20 +6,32 @@ module RuboCop 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  module Rails
         
     | 
| 
       7 
7 
     | 
    
         
             
                    # Checks that tests use `have_http_status` instead of equality matchers.
         
     | 
| 
       8 
8 
     | 
    
         
             
                    #
         
     | 
| 
       9 
     | 
    
         
            -
                    # @example
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # @example ResponseMethods: ['response', 'last_response'] (default)
         
     | 
| 
       10 
10 
     | 
    
         
             
                    #   # bad
         
     | 
| 
       11 
11 
     | 
    
         
             
                    #   expect(response.status).to be(200)
         
     | 
| 
       12 
     | 
    
         
            -
                    #   expect( 
     | 
| 
      
 12 
     | 
    
         
            +
                    #   expect(last_response.code).to eq("200")
         
     | 
| 
       13 
13 
     | 
    
         
             
                    #
         
     | 
| 
       14 
14 
     | 
    
         
             
                    #   # good
         
     | 
| 
       15 
15 
     | 
    
         
             
                    #   expect(response).to have_http_status(200)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    #   expect(last_response).to have_http_status(200)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    #
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # @example ResponseMethods: ['foo_response']
         
     | 
| 
      
 19 
     | 
    
         
            +
                    #   # bad
         
     | 
| 
      
 20 
     | 
    
         
            +
                    #   expect(foo_response.status).to be(200)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    #
         
     | 
| 
      
 22 
     | 
    
         
            +
                    #   # good
         
     | 
| 
      
 23 
     | 
    
         
            +
                    #   expect(foo_response).to have_http_status(200)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    #
         
     | 
| 
      
 25 
     | 
    
         
            +
                    #   # also good
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #   expect(response).to have_http_status(200)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    #   expect(last_response).to have_http_status(200)
         
     | 
| 
       16 
28 
     | 
    
         
             
                    #
         
     | 
| 
       17 
29 
     | 
    
         
             
                    class HaveHttpStatus < ::RuboCop::Cop::Base
         
     | 
| 
       18 
30 
     | 
    
         
             
                      extend AutoCorrector
         
     | 
| 
       19 
31 
     | 
    
         | 
| 
       20 
32 
     | 
    
         
             
                      MSG =
         
     | 
| 
       21 
     | 
    
         
            -
                        'Prefer `expect(response).%<to>s  
     | 
| 
       22 
     | 
    
         
            -
                        'over `%<bad_code>s`.'
         
     | 
| 
      
 33 
     | 
    
         
            +
                        'Prefer `expect(%<response>s).%<to>s ' \
         
     | 
| 
      
 34 
     | 
    
         
            +
                        'have_http_status(%<status>s)` over `%<bad_code>s`.'
         
     | 
| 
       23 
35 
     | 
    
         | 
| 
       24 
36 
     | 
    
         
             
                      RUNNERS = %i[to to_not not_to].to_set
         
     | 
| 
       25 
37 
     | 
    
         
             
                      RESTRICT_ON_SEND = RUNNERS
         
     | 
| 
         @@ -28,26 +40,38 @@ module RuboCop 
     | 
|
| 
       28 
40 
     | 
    
         
             
                      def_node_matcher :match_status, <<~PATTERN
         
     | 
| 
       29 
41 
     | 
    
         
             
                        (send
         
     | 
| 
       30 
42 
     | 
    
         
             
                          (send nil? :expect
         
     | 
| 
       31 
     | 
    
         
            -
                            $(send (send nil?  
     | 
| 
      
 43 
     | 
    
         
            +
                            $(send $(send nil? #response_methods?) {:status :code})
         
     | 
| 
       32 
44 
     | 
    
         
             
                          )
         
     | 
| 
       33 
45 
     | 
    
         
             
                          $RUNNERS
         
     | 
| 
       34 
46 
     | 
    
         
             
                          $(send nil? {:be :eq :eql :equal} ({int str} $_))
         
     | 
| 
       35 
47 
     | 
    
         
             
                        )
         
     | 
| 
       36 
48 
     | 
    
         
             
                      PATTERN
         
     | 
| 
       37 
49 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
                      def on_send(node)
         
     | 
| 
       39 
     | 
    
         
            -
                        match_status(node) do 
     | 
| 
      
 50 
     | 
    
         
            +
                      def on_send(node) # rubocop:disable Metrics/MethodLength
         
     | 
| 
      
 51 
     | 
    
         
            +
                        match_status(node) do
         
     | 
| 
      
 52 
     | 
    
         
            +
                          |response_status, response_method, to, match, status|
         
     | 
| 
       40 
53 
     | 
    
         
             
                          return unless status.to_s.match?(/\A\d+\z/)
         
     | 
| 
       41 
54 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                          message = format(MSG,  
     | 
| 
      
 55 
     | 
    
         
            +
                          message = format(MSG, response: response_method.method_name,
         
     | 
| 
      
 56 
     | 
    
         
            +
                                                to: to, status: status,
         
     | 
| 
       43 
57 
     | 
    
         
             
                                                bad_code: node.source)
         
     | 
| 
       44 
58 
     | 
    
         
             
                          add_offense(node, message: message) do |corrector|
         
     | 
| 
       45 
     | 
    
         
            -
                            corrector.replace(response_status,  
     | 
| 
      
 59 
     | 
    
         
            +
                            corrector.replace(response_status, response_method.method_name)
         
     | 
| 
       46 
60 
     | 
    
         
             
                            corrector.replace(match.loc.selector, 'have_http_status')
         
     | 
| 
       47 
61 
     | 
    
         
             
                            corrector.replace(match.first_argument, status.to_s)
         
     | 
| 
       48 
62 
     | 
    
         
             
                          end
         
     | 
| 
       49 
63 
     | 
    
         
             
                        end
         
     | 
| 
       50 
64 
     | 
    
         
             
                      end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                      private
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                      def response_methods?(name)
         
     | 
| 
      
 69 
     | 
    
         
            +
                        response_methods.include?(name.to_s)
         
     | 
| 
      
 70 
     | 
    
         
            +
                      end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                      def response_methods
         
     | 
| 
      
 73 
     | 
    
         
            +
                        cop_config.fetch('ResponseMethods', [])
         
     | 
| 
      
 74 
     | 
    
         
            +
                      end
         
     | 
| 
       51 
75 
     | 
    
         
             
                    end
         
     | 
| 
       52 
76 
     | 
    
         
             
                  end
         
     | 
| 
       53 
77 
     | 
    
         
             
                end
         
     | 
| 
         @@ -4,54 +4,346 @@ module RuboCop 
     | 
|
| 
       4 
4 
     | 
    
         
             
              module Cop
         
     | 
| 
       5 
5 
     | 
    
         
             
                module RSpec
         
     | 
| 
       6 
6 
     | 
    
         
             
                  module Rails
         
     | 
| 
       7 
     | 
    
         
            -
                    # Check if using Minitest matchers.
         
     | 
| 
      
 7 
     | 
    
         
            +
                    # Check if using Minitest-like matchers.
         
     | 
| 
      
 8 
     | 
    
         
            +
                    #
         
     | 
| 
      
 9 
     | 
    
         
            +
                    # Check the use of minitest-like matchers
         
     | 
| 
      
 10 
     | 
    
         
            +
                    # starting with `assert_` or `refute_`.
         
     | 
| 
       8 
11 
     | 
    
         
             
                    #
         
     | 
| 
       9 
12 
     | 
    
         
             
                    # @example
         
     | 
| 
       10 
13 
     | 
    
         
             
                    #   # bad
         
     | 
| 
       11 
14 
     | 
    
         
             
                    #   assert_equal(a, b)
         
     | 
| 
       12 
15 
     | 
    
         
             
                    #   assert_equal a, b, "must be equal"
         
     | 
| 
      
 16 
     | 
    
         
            +
                    #   assert_not_includes a, b
         
     | 
| 
       13 
17 
     | 
    
         
             
                    #   refute_equal(a, b)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    #   assert_nil a
         
     | 
| 
      
 19 
     | 
    
         
            +
                    #   refute_empty(b)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    #   assert_true(a)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    #   assert_false(a)
         
     | 
| 
       14 
22 
     | 
    
         
             
                    #
         
     | 
| 
       15 
23 
     | 
    
         
             
                    #   # good
         
     | 
| 
       16 
24 
     | 
    
         
             
                    #   expect(b).to eq(a)
         
     | 
| 
       17 
25 
     | 
    
         
             
                    #   expect(b).to(eq(a), "must be equal")
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #   expect(a).not_to include(b)
         
     | 
| 
       18 
27 
     | 
    
         
             
                    #   expect(b).not_to eq(a)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    #   expect(a).to eq(nil)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    #   expect(a).not_to be_empty
         
     | 
| 
      
 30 
     | 
    
         
            +
                    #   expect(a).to be(true)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #   expect(a).to be(false)
         
     | 
| 
       19 
32 
     | 
    
         
             
                    #
         
     | 
| 
       20 
33 
     | 
    
         
             
                    class MinitestAssertions < Base
         
     | 
| 
       21 
34 
     | 
    
         
             
                      extend AutoCorrector
         
     | 
| 
       22 
35 
     | 
    
         | 
| 
      
 36 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 37 
     | 
    
         
            +
                      class BasicAssertion
         
     | 
| 
      
 38 
     | 
    
         
            +
                        extend NodePattern::Macros
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                        attr_reader :expected, :actual, :failure_message
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                        def self.minitest_assertion
         
     | 
| 
      
 43 
     | 
    
         
            +
                          raise NotImplementedError
         
     | 
| 
      
 44 
     | 
    
         
            +
                        end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                        def initialize(expected, actual, failure_message)
         
     | 
| 
      
 47 
     | 
    
         
            +
                          @expected = expected&.source
         
     | 
| 
      
 48 
     | 
    
         
            +
                          @actual = actual.source
         
     | 
| 
      
 49 
     | 
    
         
            +
                          @failure_message = failure_message&.source
         
     | 
| 
      
 50 
     | 
    
         
            +
                        end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                        def replaced(node)
         
     | 
| 
      
 53 
     | 
    
         
            +
                          runner = negated?(node) ? 'not_to' : 'to'
         
     | 
| 
      
 54 
     | 
    
         
            +
                          if failure_message.nil?
         
     | 
| 
      
 55 
     | 
    
         
            +
                            "expect(#{actual}).#{runner} #{assertion}"
         
     | 
| 
      
 56 
     | 
    
         
            +
                          else
         
     | 
| 
      
 57 
     | 
    
         
            +
                            "expect(#{actual}).#{runner}(#{assertion}, #{failure_message})"
         
     | 
| 
      
 58 
     | 
    
         
            +
                          end
         
     | 
| 
      
 59 
     | 
    
         
            +
                        end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                        def negated?(node)
         
     | 
| 
      
 62 
     | 
    
         
            +
                          node.method_name.start_with?('assert_not_', 'refute_')
         
     | 
| 
      
 63 
     | 
    
         
            +
                        end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 66 
     | 
    
         
            +
                          raise NotImplementedError
         
     | 
| 
      
 67 
     | 
    
         
            +
                        end
         
     | 
| 
      
 68 
     | 
    
         
            +
                      end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 71 
     | 
    
         
            +
                      class EqualAssertion < BasicAssertion
         
     | 
| 
      
 72 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 73 
     | 
    
         
            +
                          assert_equal
         
     | 
| 
      
 74 
     | 
    
         
            +
                          assert_not_equal
         
     | 
| 
      
 75 
     | 
    
         
            +
                          refute_equal
         
     | 
| 
      
 76 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 79 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 80 
     | 
    
         
            +
                          (send nil? {:assert_equal :assert_not_equal :refute_equal} $_ $_ $_?)
         
     | 
| 
      
 81 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                        def self.match(expected, actual, failure_message)
         
     | 
| 
      
 84 
     | 
    
         
            +
                          new(expected, actual, failure_message.first)
         
     | 
| 
      
 85 
     | 
    
         
            +
                        end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 88 
     | 
    
         
            +
                          "eq(#{expected})"
         
     | 
| 
      
 89 
     | 
    
         
            +
                        end
         
     | 
| 
      
 90 
     | 
    
         
            +
                      end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 93 
     | 
    
         
            +
                      class KindOfAssertion < BasicAssertion
         
     | 
| 
      
 94 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 95 
     | 
    
         
            +
                          assert_kind_of
         
     | 
| 
      
 96 
     | 
    
         
            +
                          assert_not_kind_of
         
     | 
| 
      
 97 
     | 
    
         
            +
                          refute_kind_of
         
     | 
| 
      
 98 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 101 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 102 
     | 
    
         
            +
                          (send nil? {:assert_kind_of :assert_not_kind_of :refute_kind_of} $_ $_ $_?)
         
     | 
| 
      
 103 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                        def self.match(expected, actual, failure_message)
         
     | 
| 
      
 106 
     | 
    
         
            +
                          new(expected, actual, failure_message.first)
         
     | 
| 
      
 107 
     | 
    
         
            +
                        end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 110 
     | 
    
         
            +
                          "be_a_kind_of(#{expected})"
         
     | 
| 
      
 111 
     | 
    
         
            +
                        end
         
     | 
| 
      
 112 
     | 
    
         
            +
                      end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 115 
     | 
    
         
            +
                      class InstanceOfAssertion < BasicAssertion
         
     | 
| 
      
 116 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 117 
     | 
    
         
            +
                          assert_instance_of
         
     | 
| 
      
 118 
     | 
    
         
            +
                          assert_not_instance_of
         
     | 
| 
      
 119 
     | 
    
         
            +
                          refute_instance_of
         
     | 
| 
      
 120 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 123 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 124 
     | 
    
         
            +
                          (send nil? {:assert_instance_of :assert_not_instance_of :refute_instance_of} $_ $_ $_?)
         
     | 
| 
      
 125 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                        def self.match(expected, actual, failure_message)
         
     | 
| 
      
 128 
     | 
    
         
            +
                          new(expected, actual, failure_message.first)
         
     | 
| 
      
 129 
     | 
    
         
            +
                        end
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 132 
     | 
    
         
            +
                          "be_an_instance_of(#{expected})"
         
     | 
| 
      
 133 
     | 
    
         
            +
                        end
         
     | 
| 
      
 134 
     | 
    
         
            +
                      end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 137 
     | 
    
         
            +
                      class IncludesAssertion < BasicAssertion
         
     | 
| 
      
 138 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 139 
     | 
    
         
            +
                          assert_includes
         
     | 
| 
      
 140 
     | 
    
         
            +
                          assert_not_includes
         
     | 
| 
      
 141 
     | 
    
         
            +
                          refute_includes
         
     | 
| 
      
 142 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 145 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 146 
     | 
    
         
            +
                          (send nil? {:assert_includes :assert_not_includes :refute_includes} $_ $_ $_?)
         
     | 
| 
      
 147 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                        def self.match(collection, expected, failure_message)
         
     | 
| 
      
 150 
     | 
    
         
            +
                          new(expected, collection, failure_message.first)
         
     | 
| 
      
 151 
     | 
    
         
            +
                        end
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 154 
     | 
    
         
            +
                          "include(#{expected})"
         
     | 
| 
      
 155 
     | 
    
         
            +
                        end
         
     | 
| 
      
 156 
     | 
    
         
            +
                      end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 159 
     | 
    
         
            +
                      class InDeltaAssertion < BasicAssertion
         
     | 
| 
      
 160 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 161 
     | 
    
         
            +
                          assert_in_delta
         
     | 
| 
      
 162 
     | 
    
         
            +
                          assert_not_in_delta
         
     | 
| 
      
 163 
     | 
    
         
            +
                          refute_in_delta
         
     | 
| 
      
 164 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 167 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 168 
     | 
    
         
            +
                          (send nil? {:assert_in_delta :assert_not_in_delta :refute_in_delta} $_ $_ $_? $_?)
         
     | 
| 
      
 169 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                        def self.match(expected, actual, delta, failure_message)
         
     | 
| 
      
 172 
     | 
    
         
            +
                          new(expected, actual, delta.first, failure_message.first)
         
     | 
| 
      
 173 
     | 
    
         
            +
                        end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                        def initialize(expected, actual, delta, fail_message)
         
     | 
| 
      
 176 
     | 
    
         
            +
                          super(expected, actual, fail_message)
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                          @delta = delta&.source || '0.001'
         
     | 
| 
      
 179 
     | 
    
         
            +
                        end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 182 
     | 
    
         
            +
                          "be_within(#{@delta}).of(#{expected})"
         
     | 
| 
      
 183 
     | 
    
         
            +
                        end
         
     | 
| 
      
 184 
     | 
    
         
            +
                      end
         
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
      
 186 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 187 
     | 
    
         
            +
                      class PredicateAssertion < BasicAssertion
         
     | 
| 
      
 188 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 189 
     | 
    
         
            +
                          assert_predicate
         
     | 
| 
      
 190 
     | 
    
         
            +
                          assert_not_predicate
         
     | 
| 
      
 191 
     | 
    
         
            +
                          refute_predicate
         
     | 
| 
      
 192 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 195 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 196 
     | 
    
         
            +
                          (send nil? {:assert_predicate :assert_not_predicate :refute_predicate} $_ ${sym} $_?)
         
     | 
| 
      
 197 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                        def self.match(subject, predicate, failure_message)
         
     | 
| 
      
 200 
     | 
    
         
            +
                          return nil unless predicate.value.end_with?('?')
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                          new(predicate, subject, failure_message.first)
         
     | 
| 
      
 203 
     | 
    
         
            +
                        end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 206 
     | 
    
         
            +
                          "be_#{expected.delete_prefix(':').delete_suffix('?')}"
         
     | 
| 
      
 207 
     | 
    
         
            +
                        end
         
     | 
| 
      
 208 
     | 
    
         
            +
                      end
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 211 
     | 
    
         
            +
                      class MatchAssertion < BasicAssertion
         
     | 
| 
      
 212 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 213 
     | 
    
         
            +
                          assert_match
         
     | 
| 
      
 214 
     | 
    
         
            +
                          refute_match
         
     | 
| 
      
 215 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 218 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 219 
     | 
    
         
            +
                          (send nil? {:assert_match :refute_match} $_ $_ $_?)
         
     | 
| 
      
 220 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                        def self.match(matcher, actual, failure_message)
         
     | 
| 
      
 223 
     | 
    
         
            +
                          new(matcher, actual, failure_message.first)
         
     | 
| 
      
 224 
     | 
    
         
            +
                        end
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 227 
     | 
    
         
            +
                          "match(#{expected})"
         
     | 
| 
      
 228 
     | 
    
         
            +
                        end
         
     | 
| 
      
 229 
     | 
    
         
            +
                      end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 232 
     | 
    
         
            +
                      class NilAssertion < BasicAssertion
         
     | 
| 
      
 233 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 234 
     | 
    
         
            +
                          assert_nil
         
     | 
| 
      
 235 
     | 
    
         
            +
                          assert_not_nil
         
     | 
| 
      
 236 
     | 
    
         
            +
                          refute_nil
         
     | 
| 
      
 237 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 240 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 241 
     | 
    
         
            +
                          (send nil? {:assert_nil :assert_not_nil :refute_nil} $_ $_?)
         
     | 
| 
      
 242 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
                        def self.match(actual, failure_message)
         
     | 
| 
      
 245 
     | 
    
         
            +
                          new(nil, actual, failure_message.first)
         
     | 
| 
      
 246 
     | 
    
         
            +
                        end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 249 
     | 
    
         
            +
                          'eq(nil)'
         
     | 
| 
      
 250 
     | 
    
         
            +
                        end
         
     | 
| 
      
 251 
     | 
    
         
            +
                      end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 254 
     | 
    
         
            +
                      class EmptyAssertion < BasicAssertion
         
     | 
| 
      
 255 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 256 
     | 
    
         
            +
                          assert_empty
         
     | 
| 
      
 257 
     | 
    
         
            +
                          assert_not_empty
         
     | 
| 
      
 258 
     | 
    
         
            +
                          refute_empty
         
     | 
| 
      
 259 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 262 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 263 
     | 
    
         
            +
                          (send nil? {:assert_empty :assert_not_empty :refute_empty} $_ $_?)
         
     | 
| 
      
 264 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 265 
     | 
    
         
            +
             
     | 
| 
      
 266 
     | 
    
         
            +
                        def self.match(actual, failure_message)
         
     | 
| 
      
 267 
     | 
    
         
            +
                          new(nil, actual, failure_message.first)
         
     | 
| 
      
 268 
     | 
    
         
            +
                        end
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 271 
     | 
    
         
            +
                          'be_empty'
         
     | 
| 
      
 272 
     | 
    
         
            +
                        end
         
     | 
| 
      
 273 
     | 
    
         
            +
                      end
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 276 
     | 
    
         
            +
                      class TrueAssertion < BasicAssertion
         
     | 
| 
      
 277 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 278 
     | 
    
         
            +
                          assert_true
         
     | 
| 
      
 279 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 282 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 283 
     | 
    
         
            +
                          (send nil? {:assert_true} $_ $_?)
         
     | 
| 
      
 284 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 285 
     | 
    
         
            +
             
     | 
| 
      
 286 
     | 
    
         
            +
                        def self.match(actual, failure_message)
         
     | 
| 
      
 287 
     | 
    
         
            +
                          new(nil, actual, failure_message.first)
         
     | 
| 
      
 288 
     | 
    
         
            +
                        end
         
     | 
| 
      
 289 
     | 
    
         
            +
             
     | 
| 
      
 290 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 291 
     | 
    
         
            +
                          'be(true)'
         
     | 
| 
      
 292 
     | 
    
         
            +
                        end
         
     | 
| 
      
 293 
     | 
    
         
            +
                      end
         
     | 
| 
      
 294 
     | 
    
         
            +
             
     | 
| 
      
 295 
     | 
    
         
            +
                      # :nodoc:
         
     | 
| 
      
 296 
     | 
    
         
            +
                      class FalseAssertion < BasicAssertion
         
     | 
| 
      
 297 
     | 
    
         
            +
                        MATCHERS = %i[
         
     | 
| 
      
 298 
     | 
    
         
            +
                          assert_false
         
     | 
| 
      
 299 
     | 
    
         
            +
                        ].freeze
         
     | 
| 
      
 300 
     | 
    
         
            +
             
     | 
| 
      
 301 
     | 
    
         
            +
                        # @!method self.minitest_assertion(node)
         
     | 
| 
      
 302 
     | 
    
         
            +
                        def_node_matcher 'self.minitest_assertion', <<~PATTERN # rubocop:disable InternalAffairs/NodeMatcherDirective
         
     | 
| 
      
 303 
     | 
    
         
            +
                          (send nil? {:assert_false} $_ $_?)
         
     | 
| 
      
 304 
     | 
    
         
            +
                        PATTERN
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
                        def self.match(actual, failure_message)
         
     | 
| 
      
 307 
     | 
    
         
            +
                          new(nil, actual, failure_message.first)
         
     | 
| 
      
 308 
     | 
    
         
            +
                        end
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
                        def assertion
         
     | 
| 
      
 311 
     | 
    
         
            +
                          'be(false)'
         
     | 
| 
      
 312 
     | 
    
         
            +
                        end
         
     | 
| 
      
 313 
     | 
    
         
            +
                      end
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
       23 
315 
     | 
    
         
             
                      MSG = 'Use `%<prefer>s`.'
         
     | 
| 
       24 
     | 
    
         
            -
                      RESTRICT_ON_SEND = %i[assert_equal refute_equal].freeze
         
     | 
| 
       25 
316 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                      #  
     | 
| 
       27 
     | 
    
         
            -
                       
     | 
| 
       28 
     | 
    
         
            -
                         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 317 
     | 
    
         
            +
                      # TODO: replace with `BasicAssertion.subclasses` in Ruby 3.1+
         
     | 
| 
      
 318 
     | 
    
         
            +
                      ASSERTION_MATCHERS = constants(false).filter_map do |c|
         
     | 
| 
      
 319 
     | 
    
         
            +
                        const = const_get(c)
         
     | 
| 
      
 320 
     | 
    
         
            +
             
     | 
| 
      
 321 
     | 
    
         
            +
                        const if const.is_a?(Class) && const.superclass == BasicAssertion
         
     | 
| 
      
 322 
     | 
    
         
            +
                      end
         
     | 
| 
      
 323 
     | 
    
         
            +
             
     | 
| 
      
 324 
     | 
    
         
            +
                      RESTRICT_ON_SEND = ASSERTION_MATCHERS.flat_map { |m| m::MATCHERS }
         
     | 
| 
       30 
325 
     | 
    
         | 
| 
       31 
326 
     | 
    
         
             
                      def on_send(node)
         
     | 
| 
       32 
     | 
    
         
            -
                         
     | 
| 
       33 
     | 
    
         
            -
                           
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                             
     | 
| 
      
 327 
     | 
    
         
            +
                        ASSERTION_MATCHERS.each do |m|
         
     | 
| 
      
 328 
     | 
    
         
            +
                          m.minitest_assertion(node) do |*args|
         
     | 
| 
      
 329 
     | 
    
         
            +
                            assertion = m.match(*args)
         
     | 
| 
      
 330 
     | 
    
         
            +
             
     | 
| 
      
 331 
     | 
    
         
            +
                            next if assertion.nil?
         
     | 
| 
      
 332 
     | 
    
         
            +
             
     | 
| 
      
 333 
     | 
    
         
            +
                            on_assertion(node, assertion)
         
     | 
| 
       37 
334 
     | 
    
         
             
                          end
         
     | 
| 
       38 
335 
     | 
    
         
             
                        end
         
     | 
| 
       39 
336 
     | 
    
         
             
                      end
         
     | 
| 
       40 
337 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                       
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                        if failure_message.nil?
         
     | 
| 
       46 
     | 
    
         
            -
                          "expect(#{actual.source}).#{runner} eq(#{expected.source})"
         
     | 
| 
       47 
     | 
    
         
            -
                        else
         
     | 
| 
       48 
     | 
    
         
            -
                          "expect(#{actual.source}).#{runner}(eq(#{expected.source}), " \
         
     | 
| 
       49 
     | 
    
         
            -
                            "#{failure_message.source})"
         
     | 
| 
      
 338 
     | 
    
         
            +
                      def on_assertion(node, assertion)
         
     | 
| 
      
 339 
     | 
    
         
            +
                        preferred = assertion.replaced(node)
         
     | 
| 
      
 340 
     | 
    
         
            +
                        add_offense(node, message: message(preferred)) do |corrector|
         
     | 
| 
      
 341 
     | 
    
         
            +
                          corrector.replace(node, preferred)
         
     | 
| 
       50 
342 
     | 
    
         
             
                        end
         
     | 
| 
       51 
343 
     | 
    
         
             
                      end
         
     | 
| 
       52 
344 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                      def message( 
     | 
| 
       54 
     | 
    
         
            -
                        format(MSG, prefer:  
     | 
| 
      
 345 
     | 
    
         
            +
                      def message(preferred)
         
     | 
| 
      
 346 
     | 
    
         
            +
                        format(MSG, prefer: preferred)
         
     | 
| 
       55 
347 
     | 
    
         
             
                      end
         
     | 
| 
       56 
348 
     | 
    
         
             
                    end
         
     | 
| 
       57 
349 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -9,10 +9,12 @@ module RuboCop 
     | 
|
| 
       9 
9 
     | 
    
         
             
                  #   # bad
         
     | 
| 
       10 
10 
     | 
    
         
             
                  #   expect(foo).to be_exist(bar)
         
     | 
| 
       11 
11 
     | 
    
         
             
                  #   expect(foo).not_to be_include(bar)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   expect(foo).to be_all(bar)
         
     | 
| 
       12 
13 
     | 
    
         
             
                  #
         
     | 
| 
       13 
14 
     | 
    
         
             
                  #   # good
         
     | 
| 
       14 
15 
     | 
    
         
             
                  #   expect(foo).to exist(bar)
         
     | 
| 
       15 
16 
     | 
    
         
             
                  #   expect(foo).not_to include(bar)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   expect(foo).to all be(bar)
         
     | 
| 
       16 
18 
     | 
    
         
             
                  #
         
     | 
| 
       17 
19 
     | 
    
         
             
                  class RedundantPredicateMatcher < Base
         
     | 
| 
       18 
20 
     | 
    
         
             
                    extend AutoCorrector
         
     | 
| 
         @@ -25,7 +27,7 @@ module RuboCop 
     | 
|
| 
       25 
27 
     | 
    
         | 
| 
       26 
28 
     | 
    
         
             
                    def on_send(node)
         
     | 
| 
       27 
29 
     | 
    
         
             
                      return if node.parent.block_type? || node.arguments.empty?
         
     | 
| 
       28 
     | 
    
         
            -
                      return unless  
     | 
| 
      
 30 
     | 
    
         
            +
                      return unless replaceable_arguments?(node)
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
       30 
32 
     | 
    
         
             
                      method_name = node.method_name.to_s
         
     | 
| 
       31 
33 
     | 
    
         
             
                      replaced = replaced_method_name(method_name)
         
     | 
| 
         @@ -43,7 +45,7 @@ module RuboCop 
     | 
|
| 
       43 
45 
     | 
    
         
             
                      format(MSG, bad: bad_method, good: good_method)
         
     | 
| 
       44 
46 
     | 
    
         
             
                    end
         
     | 
| 
       45 
47 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
                    def  
     | 
| 
      
 48 
     | 
    
         
            +
                    def replaceable_arguments?(node)
         
     | 
| 
       47 
49 
     | 
    
         
             
                      if node.method?(:be_all)
         
     | 
| 
       48 
50 
     | 
    
         
             
                        node.first_argument.send_type?
         
     | 
| 
       49 
51 
     | 
    
         
             
                      else
         
     | 
| 
         @@ -0,0 +1,120 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module RuboCop
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Cop
         
     | 
| 
      
 5 
     | 
    
         
            +
                module RSpec
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Checks for repeated calls to subject missing that it is memoized.
         
     | 
| 
      
 7 
     | 
    
         
            +
                  #
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # @example
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #   # bad
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #   it do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #     subject
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #     expect { subject }.to not_change { A.count }
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   it do
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #     expect { subject }.to change { A.count }
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #     expect { subject }.to not_change { A.count }
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   # good
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #   it do
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #     expect { my_method }.to change { A.count }
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     expect { my_method }.to not_change { A.count }
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  class RepeatedSubjectCall < Base
         
     | 
| 
      
 27 
     | 
    
         
            +
                    include TopLevelGroup
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    MSG = 'Calls to subject are memoized, this block is misleading'
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    # @!method subject?(node)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    #   Find a named or unnamed subject definition
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #   @example anonymous subject
         
     | 
| 
      
 35 
     | 
    
         
            +
                    #     subject?(parse('subject { foo }').ast) do |name|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    #       name # => :subject
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #     end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #   @example named subject
         
     | 
| 
      
 40 
     | 
    
         
            +
                    #     subject?(parse('subject(:thing) { foo }').ast) do |name|
         
     | 
| 
      
 41 
     | 
    
         
            +
                    #       name # => :thing
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #     end
         
     | 
| 
      
 43 
     | 
    
         
            +
                    #
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #   @param node [RuboCop::AST::Node]
         
     | 
| 
      
 45 
     | 
    
         
            +
                    #
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #   @yield [Symbol] subject name
         
     | 
| 
      
 47 
     | 
    
         
            +
                    def_node_matcher :subject?, <<-PATTERN
         
     | 
| 
      
 48 
     | 
    
         
            +
                      (block
         
     | 
| 
      
 49 
     | 
    
         
            +
                        (send nil?
         
     | 
| 
      
 50 
     | 
    
         
            +
                          { #Subjects.all (sym $_) | $#Subjects.all }
         
     | 
| 
      
 51 
     | 
    
         
            +
                        ) args ...)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    # @!method subject_calls(node, method_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    def_node_search :subject_calls, <<~PATTERN
         
     | 
| 
      
 56 
     | 
    
         
            +
                      (send nil? %)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    PATTERN
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    def on_top_level_group(node)
         
     | 
| 
      
 60 
     | 
    
         
            +
                      @subjects_by_node = detect_subjects_in_scope(node)
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      detect_offenses_in_block(node)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    private
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    def detect_offense(example_node, subject_node)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      walker = subject_node
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      while walker.parent? && walker.parent != example_node.body
         
     | 
| 
      
 71 
     | 
    
         
            +
                        walker = walker.parent
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                        if walker.block_type? && walker.method?(:expect)
         
     | 
| 
      
 74 
     | 
    
         
            +
                          add_offense(walker)
         
     | 
| 
      
 75 
     | 
    
         
            +
                          return
         
     | 
| 
      
 76 
     | 
    
         
            +
                        end
         
     | 
| 
      
 77 
     | 
    
         
            +
                      end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    def detect_offenses_in_block(node, subject_names = [])
         
     | 
| 
      
 81 
     | 
    
         
            +
                      subject_names = [*subject_names, *@subjects_by_node[node]]
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                      if example?(node)
         
     | 
| 
      
 84 
     | 
    
         
            +
                        return detect_offenses_in_example(node, subject_names)
         
     | 
| 
      
 85 
     | 
    
         
            +
                      end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                      node.each_child_node(:send, :def, :block, :begin) do |child|
         
     | 
| 
      
 88 
     | 
    
         
            +
                        detect_offenses_in_block(child, subject_names)
         
     | 
| 
      
 89 
     | 
    
         
            +
                      end
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    def detect_offenses_in_example(node, subject_names)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      return unless node.body
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                      subjects_used = Hash.new(false)
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                      subject_calls(node.body, Set[*subject_names, :subject]).each do |call|
         
     | 
| 
      
 98 
     | 
    
         
            +
                        if subjects_used[call.method_name]
         
     | 
| 
      
 99 
     | 
    
         
            +
                          detect_offense(node, call)
         
     | 
| 
      
 100 
     | 
    
         
            +
                        else
         
     | 
| 
      
 101 
     | 
    
         
            +
                          subjects_used[call.method_name] = true
         
     | 
| 
      
 102 
     | 
    
         
            +
                        end
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    def detect_subjects_in_scope(node)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      node.each_descendant(:block).with_object({}) do |child, h|
         
     | 
| 
      
 108 
     | 
    
         
            +
                        subject?(child) do |name|
         
     | 
| 
      
 109 
     | 
    
         
            +
                          outer_example_group = child.each_ancestor(:block).find do |a|
         
     | 
| 
      
 110 
     | 
    
         
            +
                            example_group?(a)
         
     | 
| 
      
 111 
     | 
    
         
            +
                          end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                          (h[outer_example_group] ||= []) << name
         
     | 
| 
      
 114 
     | 
    
         
            +
                        end
         
     | 
| 
      
 115 
     | 
    
         
            +
                      end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -78,6 +78,7 @@ require_relative 'rspec/implicit_subject' 
     | 
|
| 
       78 
78 
     | 
    
         
             
            require_relative 'rspec/indexed_let'
         
     | 
| 
       79 
79 
     | 
    
         
             
            require_relative 'rspec/instance_spy'
         
     | 
| 
       80 
80 
     | 
    
         
             
            require_relative 'rspec/instance_variable'
         
     | 
| 
      
 81 
     | 
    
         
            +
            require_relative 'rspec/is_expected_specify'
         
     | 
| 
       81 
82 
     | 
    
         
             
            require_relative 'rspec/it_behaves_like'
         
     | 
| 
       82 
83 
     | 
    
         
             
            require_relative 'rspec/iterated_expectation'
         
     | 
| 
       83 
84 
     | 
    
         
             
            require_relative 'rspec/leading_subject'
         
     | 
| 
         @@ -113,6 +114,7 @@ require_relative 'rspec/repeated_example' 
     | 
|
| 
       113 
114 
     | 
    
         
             
            require_relative 'rspec/repeated_example_group_body'
         
     | 
| 
       114 
115 
     | 
    
         
             
            require_relative 'rspec/repeated_example_group_description'
         
     | 
| 
       115 
116 
     | 
    
         
             
            require_relative 'rspec/repeated_include_example'
         
     | 
| 
      
 117 
     | 
    
         
            +
            require_relative 'rspec/repeated_subject_call'
         
     | 
| 
       116 
118 
     | 
    
         
             
            require_relative 'rspec/return_from_stub'
         
     | 
| 
       117 
119 
     | 
    
         
             
            require_relative 'rspec/scattered_let'
         
     | 
| 
       118 
120 
     | 
    
         
             
            require_relative 'rspec/scattered_setup'
         
     | 
    
        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: 2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 2.27.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: 2024- 
     | 
| 
      
 13 
     | 
    
         
            +
            date: 2024-02-29 00:00:00.000000000 Z
         
     | 
| 
       14 
14 
     | 
    
         
             
            dependencies:
         
     | 
| 
       15 
15 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       16 
16 
     | 
    
         
             
              name: rubocop
         
     | 
| 
         @@ -139,6 +139,7 @@ files: 
     | 
|
| 
       139 
139 
     | 
    
         
             
            - lib/rubocop/cop/rspec/indexed_let.rb
         
     | 
| 
       140 
140 
     | 
    
         
             
            - lib/rubocop/cop/rspec/instance_spy.rb
         
     | 
| 
       141 
141 
     | 
    
         
             
            - lib/rubocop/cop/rspec/instance_variable.rb
         
     | 
| 
      
 142 
     | 
    
         
            +
            - lib/rubocop/cop/rspec/is_expected_specify.rb
         
     | 
| 
       142 
143 
     | 
    
         
             
            - lib/rubocop/cop/rspec/it_behaves_like.rb
         
     | 
| 
       143 
144 
     | 
    
         
             
            - lib/rubocop/cop/rspec/iterated_expectation.rb
         
     | 
| 
       144 
145 
     | 
    
         
             
            - lib/rubocop/cop/rspec/leading_subject.rb
         
     | 
| 
         @@ -192,6 +193,7 @@ files: 
     | 
|
| 
       192 
193 
     | 
    
         
             
            - lib/rubocop/cop/rspec/repeated_example_group_body.rb
         
     | 
| 
       193 
194 
     | 
    
         
             
            - lib/rubocop/cop/rspec/repeated_example_group_description.rb
         
     | 
| 
       194 
195 
     | 
    
         
             
            - lib/rubocop/cop/rspec/repeated_include_example.rb
         
     | 
| 
      
 196 
     | 
    
         
            +
            - lib/rubocop/cop/rspec/repeated_subject_call.rb
         
     | 
| 
       195 
197 
     | 
    
         
             
            - lib/rubocop/cop/rspec/return_from_stub.rb
         
     | 
| 
       196 
198 
     | 
    
         
             
            - lib/rubocop/cop/rspec/scattered_let.rb
         
     | 
| 
       197 
199 
     | 
    
         
             
            - lib/rubocop/cop/rspec/scattered_setup.rb
         
     | 
| 
         @@ -251,7 +253,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       251 
253 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       252 
254 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       253 
255 
     | 
    
         
             
            requirements: []
         
     | 
| 
       254 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 256 
     | 
    
         
            +
            rubygems_version: 3.5.3
         
     | 
| 
       255 
257 
     | 
    
         
             
            signing_key:
         
     | 
| 
       256 
258 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       257 
259 
     | 
    
         
             
            summary: Code style checking for RSpec files
         
     |