rubocop-rspec 2.25.0 → 2.27.1

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +35 -0
  3. data/config/default.yml +39 -4
  4. data/lib/rubocop/cop/rspec/around_block.rb +3 -3
  5. data/lib/rubocop/cop/rspec/be.rb +1 -1
  6. data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
  7. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  8. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  9. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  10. data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
  11. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -2
  12. data/lib/rubocop/cop/rspec/change_by_zero.rb +30 -4
  13. data/lib/rubocop/cop/rspec/context_method.rb +2 -2
  14. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  15. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  16. data/lib/rubocop/cop/rspec/described_class.rb +33 -11
  17. data/lib/rubocop/cop/rspec/empty_example_group.rb +2 -2
  18. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
  19. data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
  20. data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
  21. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -1
  22. data/lib/rubocop/cop/rspec/expect_actual.rb +5 -2
  23. data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
  24. data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
  25. data/lib/rubocop/cop/rspec/focus.rb +2 -2
  26. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  27. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  28. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
  29. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  30. data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
  31. data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
  32. data/lib/rubocop/cop/rspec/instance_variable.rb +2 -2
  33. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  34. data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
  35. data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
  36. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  37. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -2
  38. data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
  39. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
  40. data/lib/rubocop/cop/rspec/multiple_expectations.rb +12 -7
  41. data/lib/rubocop/cop/rspec/pending.rb +1 -1
  42. data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
  43. data/lib/rubocop/cop/rspec/predicate_matcher.rb +6 -6
  44. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -1
  45. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +34 -10
  46. data/lib/rubocop/cop/rspec/rails/http_status.rb +1 -1
  47. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +314 -22
  48. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  49. data/lib/rubocop/cop/rspec/receive_messages.rb +1 -1
  50. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  51. data/lib/rubocop/cop/rspec/remove_const.rb +40 -0
  52. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
  53. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
  54. data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
  55. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +124 -0
  56. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  57. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  58. data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
  59. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -3
  60. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  61. data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
  62. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
  63. data/lib/rubocop/cop/rspec/variable_definition.rb +2 -2
  64. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  65. data/lib/rubocop/cop/rspec/void_expect.rb +2 -2
  66. data/lib/rubocop/cop/rspec_cops.rb +4 -0
  67. data/lib/rubocop/rspec/language.rb +8 -8
  68. data/lib/rubocop/rspec/version.rb +1 -1
  69. data/lib/rubocop/rspec/wording.rb +8 -0
  70. metadata +10 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: afb5cf0c88af50530fc023549eb7e9083db3ca2314553f1fca912ea2a3c47bdb
4
- data.tar.gz: ba30f926d9cd3056e786f724140ac4358af527496b2e783fcc5c8e541adc9d13
3
+ metadata.gz: 629669f9a1d9d41dae7bc62c630f3a6cdae80623d3213c8e79ac8eb207f79e59
4
+ data.tar.gz: 5005161c3684fcd8b794284bcaeedc50253a545d649be9e0e0a82958fdd125b0
5
5
  SHA512:
6
- metadata.gz: cd7955549b3b95b481eb5dea24b98f72e1b802546f8e090695a782a20b0ee8d1d0bcca9a6089f3a46718c5a14967de6c8473709ecea72d8aa5eae85e2cb5bd6f
7
- data.tar.gz: 2beb193cf65884c363c2ece1a3a7ba5db39eecc3a579724bd39f29696ab29276def06261363b6a6290d378743e0b79d414e056b65f0a8a9e2e4215a417d9b978
6
+ metadata.gz: 8af331d4b31697228397a1122cbe331223d3f040f44d2260e5148d73177df5ba08ae2aea1d481b27f698b5d59c9522d1c5f4c5e08e14b473a548de75ccf218c6
7
+ data.tar.gz: 0f86ea257bcd14e73c57b2e431f618e0959cf9dbeca6e22c4b75bd8d4945f670d8fdb65212ff12969b351b62c35f845ca28041d4b1b30627e7d64dda5932b365
data/CHANGELOG.md CHANGED
@@ -2,6 +2,35 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.27.1 (2024-03-03)
6
+
7
+ - Fix a false positive for `RSpec/RepeatedSubjectCall` when `subject.method_call`. ([@ydah])
8
+ - Add configuration option `OnlyStaticConstants` to `RSpec/DescribedClass`. ([@ydah])
9
+
10
+ ## 2.27.0 (2024-03-01)
11
+
12
+ - Add new `RSpec/IsExpectedSpecify` cop. ([@ydah])
13
+ - Add new `RSpec/RepeatedSubjectCall` cop. ([@drcapulet])
14
+ - 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])
15
+ - Support asserts with messages in `Rspec/BeEmpty`. ([@G-Rath])
16
+ - Fix a false positive for `RSpec/ExpectActual` when used with rspec-rails routing matchers. ([@naveg])
17
+ - Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
18
+ - Fix a false negative for `RSpec/DescribedClass` when class with constant. ([@ydah])
19
+ - Fix a false positive for `RSpec/ExampleWithoutDescription` when `specify` with multi-line block and missing description. ([@ydah])
20
+ - Fix an incorrect autocorrect for `RSpec/ChangeByZero` when compound expectations with line break before `.by(0)`. ([@ydah])
21
+
22
+ ## 2.26.1 (2024-01-05)
23
+
24
+ - Fix an error for `RSpec/SharedExamples` when using examples without argument. ([@ydah])
25
+
26
+ ## 2.26.0 (2024-01-04)
27
+
28
+ - Add new `RSpec/RedundantPredicateMatcher` cop. ([@ydah])
29
+ - Add new `RSpec/RemoveConst` cop. ([@swelther])
30
+ - Add support for correcting "it will" (future tense) for `RSpec/ExampleWording`. ([@jdufresne])
31
+ - Add support for `symbol` style for `RSpec/SharedExamples`. ([@jessieay])
32
+ - Ensure `PendingWithoutReason` can detect violations inside shared groups. ([@robinaugh])
33
+
5
34
  ## 2.25.0 (2023-10-27)
6
35
 
7
36
  - Add support single quoted string and percent string and heredoc for `RSpec/Rails/HttpStatus`. ([@ydah])
@@ -833,6 +862,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
833
862
  [@deivid-rodriguez]: https://github.com/deivid-rodriguez
834
863
  [@dgollahon]: https://github.com/dgollahon
835
864
  [@dmitrytsepelev]: https://github.com/dmitrytsepelev
865
+ [@drcapulet]: https://github.com/drcapulet
836
866
  [@drowze]: https://github.com/Drowze
837
867
  [@dswij]: https://github.com/dswij
838
868
  [@dvandersluis]: https://github.com/dvandersluis
@@ -855,7 +885,9 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
855
885
  [@ignaciovillaverde]: https://github.com/ignaciovillaverde
856
886
  [@jaredbeck]: https://github.com/jaredbeck
857
887
  [@jaredmoody]: https://github.com/jaredmoody
888
+ [@jdufresne]: https://github.com/jdufresne
858
889
  [@jeffreyc]: https://github.com/jeffreyc
890
+ [@jessieay]: https://github.com/jessieay
859
891
  [@jfragoulis]: https://github.com/jfragoulis
860
892
  [@johnny-miyake]: https://github.com/johnny-miyake
861
893
  [@jojos003]: https://github.com/jojos003
@@ -878,6 +910,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
878
910
  [@mockdeep]: https://github.com/mockdeep
879
911
  [@mothonmars]: https://github.com/MothOnMars
880
912
  [@mvz]: https://github.com/mvz
913
+ [@naveg]: https://github.com/naveg
881
914
  [@nc-holodakg]: https://github.com/nc-holodakg
882
915
  [@nevir]: https://github.com/nevir
883
916
  [@ngouy]: https://github.com/ngouy
@@ -897,6 +930,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
897
930
  [@rafix02]: https://github.com/Rafix02
898
931
  [@redross]: https://github.com/redross
899
932
  [@renanborgescampos]: https://github.com/renanborgescampos
933
+ [@robinaugh]: https://github.com/robinaugh
900
934
  [@robotdana]: https://github.com/robotdana
901
935
  [@rolfschmidt]: https://github.com/rolfschmidt
902
936
  [@rrosenblum]: https://github.com/rrosenblum
@@ -909,6 +943,7 @@ Compatibility release so users can upgrade RuboCop to 0.51.0. No new features.
909
943
  [@smcgivern]: https://github.com/smcgivern
910
944
  [@splattael]: https://github.com/splattael
911
945
  [@stephannv]: https://github.com/stephannv
946
+ [@swelther]: https://github.com/swelther
912
947
  [@t3h2mas]: https://github.com/t3h2mas
913
948
  [@tdeo]: https://github.com/tdeo
914
949
  [@tejasbubane]: https://github.com/tejasbubane
data/config/default.yml CHANGED
@@ -178,7 +178,7 @@ RSpec/BeNil:
178
178
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/BeNil
179
179
 
180
180
  RSpec/BeforeAfterAll:
181
- Description: Check that before/after(:all) isn't being used.
181
+ Description: Check that before/after(:all/:context) isn't being used.
182
182
  Enabled: true
183
183
  Exclude:
184
184
  - "**/spec/spec_helper.rb"
@@ -283,9 +283,10 @@ RSpec/DescribedClass:
283
283
  SupportedStyles:
284
284
  - described_class
285
285
  - explicit
286
+ OnlyStaticConstants: true
286
287
  SafeAutoCorrect: false
287
288
  VersionAdded: '1.0'
288
- VersionChanged: '1.11'
289
+ VersionChanged: '2.27'
289
290
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/DescribedClass
290
291
 
291
292
  RSpec/DescribedClassModuleWrapping:
@@ -548,6 +549,13 @@ RSpec/InstanceVariable:
548
549
  StyleGuide: https://rspec.rubystyle.guide/#instance-variables
549
550
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/InstanceVariable
550
551
 
552
+ RSpec/IsExpectedSpecify:
553
+ Description: Check for `specify` with `is_expected` and one-liner expectations.
554
+ Enabled: pending
555
+ VersionAdded: '2.27'
556
+ StyleGuide: https://rspec.rubystyle.guide/#it-and-specify
557
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/IsExpectedSpecify
558
+
551
559
  RSpec/ItBehavesLike:
552
560
  Description: Checks that only one `it_behaves_like` style is used.
553
561
  Enabled: true
@@ -771,6 +779,18 @@ RSpec/RedundantAround:
771
779
  VersionAdded: '2.19'
772
780
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantAround
773
781
 
782
+ RSpec/RedundantPredicateMatcher:
783
+ Description: Checks for redundant predicate matcher.
784
+ Enabled: pending
785
+ VersionAdded: '2.26'
786
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RedundantPredicateMatcher
787
+
788
+ RSpec/RemoveConst:
789
+ Description: Checks that `remove_const` is not used in specs.
790
+ Enabled: pending
791
+ VersionAdded: '2.26'
792
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RemoveConst
793
+
774
794
  RSpec/RepeatedDescription:
775
795
  Description: Check for repeated description strings in example groups.
776
796
  Enabled: true
@@ -801,6 +821,12 @@ RSpec/RepeatedIncludeExample:
801
821
  VersionAdded: '1.44'
802
822
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedIncludeExample
803
823
 
824
+ RSpec/RepeatedSubjectCall:
825
+ Description: Checks for repeated calls to subject missing that it is memoized.
826
+ Enabled: pending
827
+ VersionAdded: '2.27'
828
+ Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/RepeatedSubjectCall
829
+
804
830
  RSpec/ReturnFromStub:
805
831
  Description: Checks for consistent style of stub's return setting.
806
832
  Enabled: true
@@ -832,9 +858,14 @@ RSpec/SharedContext:
832
858
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SharedContext
833
859
 
834
860
  RSpec/SharedExamples:
835
- Description: Enforces use of string to titleize shared examples.
861
+ Description: Checks for consistent style for shared example names.
836
862
  Enabled: true
863
+ EnforcedStyle: string
864
+ SupportedStyles:
865
+ - string
866
+ - symbol
837
867
  VersionAdded: '1.25'
868
+ VersionChanged: '2.26'
838
869
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SharedExamples
839
870
 
840
871
  RSpec/SingleArgumentMessageChain:
@@ -1109,8 +1140,12 @@ RSpec/Rails/AvoidSetupHook:
1109
1140
  RSpec/Rails/HaveHttpStatus:
1110
1141
  Description: Checks that tests use `have_http_status` instead of equality matchers.
1111
1142
  Enabled: pending
1143
+ ResponseMethods:
1144
+ - response
1145
+ - last_response
1112
1146
  SafeAutoCorrect: false
1113
1147
  VersionAdded: '2.12'
1148
+ VersionChanged: '2.27'
1114
1149
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HaveHttpStatus
1115
1150
 
1116
1151
  RSpec/Rails/HttpStatus:
@@ -1149,7 +1184,7 @@ RSpec/Rails/InferredSpecType:
1149
1184
  views: view
1150
1185
 
1151
1186
  RSpec/Rails/MinitestAssertions:
1152
- Description: Check if using Minitest matchers.
1187
+ Description: Check if using Minitest-like matchers.
1153
1188
  Enabled: pending
1154
1189
  VersionAdded: '2.17'
1155
1190
  Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/MinitestAssertions
@@ -32,17 +32,17 @@ module RuboCop
32
32
  'or `%<arg>s.run`.'
33
33
 
34
34
  # @!method hook_block(node)
35
- def_node_matcher :hook_block, <<-PATTERN
35
+ def_node_matcher :hook_block, <<~PATTERN
36
36
  (block (send nil? :around sym ?) (args $...) ...)
37
37
  PATTERN
38
38
 
39
39
  # @!method hook_numblock(node)
40
- def_node_matcher :hook_numblock, <<-PATTERN
40
+ def_node_matcher :hook_numblock, <<~PATTERN
41
41
  (numblock (send nil? :around sym ?) ...)
42
42
  PATTERN
43
43
 
44
44
  # @!method find_arg_usage(node)
45
- def_node_search :find_arg_usage, <<-PATTERN
45
+ def_node_search :find_arg_usage, <<~PATTERN
46
46
  {(send $... {:call :run}) (send _ _ $...) (yield $...) (block-pass $...)}
47
47
  PATTERN
48
48
 
@@ -24,7 +24,7 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = Runners.all
25
25
 
26
26
  # @!method be_without_args(node)
27
- def_node_matcher :be_without_args, <<-PATTERN
27
+ def_node_matcher :be_without_args, <<~PATTERN
28
28
  (send _ #Runners.all $(send nil? :be))
29
29
  PATTERN
30
30
 
@@ -28,6 +28,7 @@ module RuboCop
28
28
  (send nil? :match_array (array))
29
29
  (send nil? :contain_exactly)
30
30
  }
31
+ _?
31
32
  )
32
33
  PATTERN
33
34
 
@@ -30,7 +30,7 @@ module RuboCop
30
30
  RESTRICT_ON_SEND = %i[eq].freeze
31
31
 
32
32
  # @!method eq_type_with_identity?(node)
33
- def_node_matcher :eq_type_with_identity?, <<-PATTERN
33
+ def_node_matcher :eq_type_with_identity?, <<~PATTERN
34
34
  (send nil? :eq {true false nil})
35
35
  PATTERN
36
36
 
@@ -44,7 +44,7 @@ module RuboCop
44
44
  RESTRICT_ON_SEND = %i[to].freeze
45
45
 
46
46
  # @!method eql_type_with_identity(node)
47
- def_node_matcher :eql_type_with_identity, <<-PATTERN
47
+ def_node_matcher :eql_type_with_identity, <<~PATTERN
48
48
  (send _ :to $(send nil? :eql {true false int float sym nil}))
49
49
  PATTERN
50
50
 
@@ -33,12 +33,12 @@ module RuboCop
33
33
  RESTRICT_ON_SEND = %i[be be_nil].freeze
34
34
 
35
35
  # @!method be_nil_matcher?(node)
36
- def_node_matcher :be_nil_matcher?, <<-PATTERN
36
+ def_node_matcher :be_nil_matcher?, <<~PATTERN
37
37
  (send nil? :be_nil)
38
38
  PATTERN
39
39
 
40
40
  # @!method nil_value_expectation?(node)
41
- def_node_matcher :nil_value_expectation?, <<-PATTERN
41
+ def_node_matcher :nil_value_expectation?, <<~PATTERN
42
42
  (send nil? :be nil)
43
43
  PATTERN
44
44
 
@@ -3,22 +3,16 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Check that before/after(:all) isn't being used.
6
+ # Check that before/after(:all/:context) isn't being used.
7
7
  #
8
8
  # @example
9
- # # bad
10
- # #
11
- # # Faster but risk of state leaking between examples
12
- # #
9
+ # # bad - Faster but risk of state leaking between examples
13
10
  # describe MyClass do
14
11
  # before(:all) { Widget.create }
15
- # after(:all) { Widget.delete_all }
12
+ # after(:context) { Widget.delete_all }
16
13
  # end
17
14
  #
18
- # # good
19
- # #
20
- # # Slower but examples are properly isolated
21
- # #
15
+ # # good - Slower but examples are properly isolated
22
16
  # describe MyClass do
23
17
  # before(:each) { Widget.create }
24
18
  # after(:each) { Widget.delete_all }
@@ -30,11 +24,11 @@ module RuboCop
30
24
  '`use_transactional_fixtures` is enabled, then records created ' \
31
25
  'in `%<hook>s` are not automatically rolled back.'
32
26
 
33
- RESTRICT_ON_SEND = %i[before after].freeze
27
+ RESTRICT_ON_SEND = Set[:before, :after].freeze
34
28
 
35
29
  # @!method before_or_after_all(node)
36
- def_node_matcher :before_or_after_all, <<-PATTERN
37
- $(send _ {:before :after} (sym {:all :context}))
30
+ def_node_matcher :before_or_after_all, <<~PATTERN
31
+ $(send _ RESTRICT_ON_SEND (sym {:all :context}))
38
32
  PATTERN
39
33
 
40
34
  def on_send(node)
@@ -58,12 +58,12 @@ module RuboCop
58
58
  }.freeze
59
59
 
60
60
  # @!method capybara_speak(node)
61
- def_node_matcher :capybara_speak, <<-PATTERN
61
+ def_node_matcher :capybara_speak, <<~PATTERN
62
62
  {#{MAP.keys.map(&:inspect).join(' ')}}
63
63
  PATTERN
64
64
 
65
65
  # @!method feature_method(node)
66
- def_node_matcher :feature_method, <<-PATTERN
66
+ def_node_matcher :feature_method, <<~PATTERN
67
67
  (block
68
68
  $(send #rspec? $#capybara_speak ...)
69
69
  ...)
@@ -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)`.'
@@ -66,14 +68,14 @@ module RuboCop
66
68
  RESTRICT_ON_SEND = CHANGE_METHODS.freeze
67
69
 
68
70
  # @!method expect_change_with_arguments(node)
69
- def_node_matcher :expect_change_with_arguments, <<-PATTERN
71
+ def_node_matcher :expect_change_with_arguments, <<~PATTERN
70
72
  (send
71
73
  $(send nil? CHANGE_METHODS ...) :by
72
74
  (int 0))
73
75
  PATTERN
74
76
 
75
77
  # @!method expect_change_with_block(node)
76
- def_node_matcher :expect_change_with_block, <<-PATTERN
78
+ def_node_matcher :expect_change_with_block, <<~PATTERN
77
79
  (send
78
80
  (block
79
81
  $(send nil? CHANGE_METHODS)
@@ -83,7 +85,7 @@ module RuboCop
83
85
  PATTERN
84
86
 
85
87
  # @!method change_nodes(node)
86
- def_node_search :change_nodes, <<-PATTERN
88
+ def_node_search :change_nodes, <<~PATTERN
87
89
  $(send nil? CHANGE_METHODS ...)
88
90
  PATTERN
89
91
 
@@ -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
- range = node.loc.dot.with(end_pos: node.source_range.end_pos)
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
 
@@ -30,7 +30,7 @@ module RuboCop
30
30
  MSG = 'Use `describe` for testing methods.'
31
31
 
32
32
  # @!method context_method(node)
33
- def_node_matcher :context_method, <<-PATTERN
33
+ def_node_matcher :context_method, <<~PATTERN
34
34
  (block
35
35
  (send #rspec? :context
36
36
  ${(str #method_name?) (dstr (str #method_name?) ...)}
@@ -38,7 +38,7 @@ module RuboCop
38
38
  ...)
39
39
  PATTERN
40
40
 
41
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
41
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
42
42
  context_method(node) do |context|
43
43
  add_offense(context) do |corrector|
44
44
  corrector.replace(node.send_node.loc.selector, 'describe')
@@ -61,7 +61,7 @@ module RuboCop
61
61
  MSG = 'Context description should match %<patterns>s.'
62
62
 
63
63
  # @!method context_wording(node)
64
- def_node_matcher :context_wording, <<-PATTERN
64
+ def_node_matcher :context_wording, <<~PATTERN
65
65
  (block (send #rspec? { :context :shared_context } $({str dstr xstr} ...) ...) ...)
66
66
  PATTERN
67
67
 
@@ -22,7 +22,7 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[describe].freeze
23
23
 
24
24
  # @!method describe_symbol?(node)
25
- def_node_matcher :describe_symbol?, <<-PATTERN
25
+ def_node_matcher :describe_symbol?, <<~PATTERN
26
26
  (send #rspec? :describe $sym ...)
27
27
  PATTERN
28
28
 
@@ -8,8 +8,10 @@ module RuboCop
8
8
  # If the first argument of describe is a class, the class is exposed to
9
9
  # each example via described_class.
10
10
  #
11
- # This cop can be configured using the `EnforcedStyle` and `SkipBlocks`
12
- # options.
11
+ # This cop can be configured using the `EnforcedStyle`, `SkipBlocks`
12
+ # and `OnlyStaticConstants` options.
13
+ # `OnlyStaticConstants` is only relevant when `EnforcedStyle` is
14
+ # `described_class`.
13
15
  #
14
16
  # @example `EnforcedStyle: described_class` (default)
15
17
  # # bad
@@ -22,6 +24,18 @@ module RuboCop
22
24
  # subject { described_class.do_something }
23
25
  # end
24
26
  #
27
+ # @example `OnlyStaticConstants: true` (default)
28
+ # # good
29
+ # describe MyClass do
30
+ # subject { MyClass::CONSTANT }
31
+ # end
32
+ #
33
+ # @example `OnlyStaticConstants: false`
34
+ # # bad
35
+ # describe MyClass do
36
+ # subject { MyClass::CONSTANT }
37
+ # end
38
+ #
25
39
  # @example `EnforcedStyle: explicit`
26
40
  # # bad
27
41
  # describe MyClass do
@@ -54,7 +68,7 @@ module RuboCop
54
68
  # end
55
69
  # end
56
70
  #
57
- class DescribedClass < Base
71
+ class DescribedClass < Base # rubocop:disable Metrics/ClassLength
58
72
  extend AutoCorrector
59
73
  include ConfigurableEnforcedStyle
60
74
  include Namespace
@@ -63,7 +77,7 @@ module RuboCop
63
77
  MSG = 'Use `%<replacement>s` instead of `%<src>s`.'
64
78
 
65
79
  # @!method common_instance_exec_closure?(node)
66
- def_node_matcher :common_instance_exec_closure?, <<-PATTERN
80
+ def_node_matcher :common_instance_exec_closure?, <<~PATTERN
67
81
  (block (send (const nil? {:Class :Module :Struct}) :new ...) ...)
68
82
  PATTERN
69
83
 
@@ -75,7 +89,7 @@ module RuboCop
75
89
  def_node_matcher :scope_changing_syntax?, '{def class module}'
76
90
 
77
91
  # @!method described_constant(node)
78
- def_node_matcher :described_constant, <<-PATTERN
92
+ def_node_matcher :described_constant, <<~PATTERN
79
93
  (block (send _ :describe $(const ...) ...) (args) $_)
80
94
  PATTERN
81
95
 
@@ -112,14 +126,17 @@ module RuboCop
112
126
 
113
127
  def find_usage(node, &block)
114
128
  yield(node) if offensive?(node)
115
-
116
- return if scope_change?(node) || node.const_type?
129
+ return if scope_change?(node) || allowed?(node)
117
130
 
118
131
  node.each_child_node do |child|
119
132
  find_usage(child, &block)
120
133
  end
121
134
  end
122
135
 
136
+ def allowed?(node)
137
+ node.const_type? && only_static_constants?
138
+ end
139
+
123
140
  def message(offense)
124
141
  if style == :described_class
125
142
  format(MSG, replacement: DESCRIBED_CLASS, src: offense)
@@ -139,6 +156,10 @@ module RuboCop
139
156
  node.block_type? && !rspec_block?(node) && cop_config['SkipBlocks']
140
157
  end
141
158
 
159
+ def only_static_constants?
160
+ cop_config.fetch('OnlyStaticConstants', true)
161
+ end
162
+
142
163
  def offensive?(node)
143
164
  if style == :described_class
144
165
  offensive_described_class?(node)
@@ -148,15 +169,15 @@ module RuboCop
148
169
  end
149
170
 
150
171
  def offensive_described_class?(node)
151
- return unless node.const_type?
172
+ return false unless node.const_type?
152
173
 
153
174
  # E.g. `described_class::CONSTANT`
154
- return if contains_described_class?(node)
175
+ return false if contains_described_class?(node)
155
176
 
156
177
  nearest_described_class, = node.each_ancestor(:block)
157
178
  .map { |ancestor| described_constant(ancestor) }.find(&:itself)
158
179
 
159
- return if nearest_described_class.equal?(node)
180
+ return false if nearest_described_class.equal?(node)
160
181
 
161
182
  full_const_name(nearest_described_class) == full_const_name(node)
162
183
  end
@@ -194,7 +215,8 @@ module RuboCop
194
215
  # const_name(s(:const, s(:const, nil, :M), :C)) # => [:M, :C]
195
216
  # const_name(s(:const, s(:cbase), :C)) # => [nil, :C]
196
217
  def const_name(node)
197
- namespace, name = *node # rubocop:disable InternalAffairs/NodeDestructuring
218
+ namespace = node.namespace
219
+ name = node.short_name
198
220
  if !namespace
199
221
  [name]
200
222
  elsif namespace.const_type?
@@ -162,7 +162,7 @@ module RuboCop
162
162
  end
163
163
 
164
164
  def conditionals_with_examples?(body)
165
- return unless body.begin_type? || body.case_type?
165
+ return false unless body.begin_type? || body.case_type?
166
166
 
167
167
  body.each_descendant(:if, :case).any? do |condition_node|
168
168
  examples_in_branches?(condition_node)
@@ -170,7 +170,7 @@ module RuboCop
170
170
  end
171
171
 
172
172
  def examples_in_branches?(condition_node)
173
- return if !condition_node.if_type? && !condition_node.case_type?
173
+ return false if !condition_node.if_type? && !condition_node.case_type?
174
174
 
175
175
  condition_node.branches.any? { |branch| examples?(branch) }
176
176
  end
@@ -71,8 +71,8 @@ module RuboCop
71
71
 
72
72
  def next_one_line_example?(node)
73
73
  next_sibling = node.right_sibling
74
- return unless next_sibling
75
- return unless example?(next_sibling)
74
+ return false unless next_sibling
75
+ return false unless example?(next_sibling)
76
76
 
77
77
  next_sibling.single_line?
78
78
  end
@@ -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
- # it '' do
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
- # it do
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