rubocop-rspec 2.21.0 → 3.0.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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +147 -9
  3. data/README.md +2 -2
  4. data/config/default.yml +159 -244
  5. data/config/obsoletion.yml +24 -0
  6. data/lib/rubocop/cop/rspec/around_block.rb +3 -3
  7. data/lib/rubocop/cop/rspec/base.rb +0 -1
  8. data/lib/rubocop/cop/rspec/be.rb +1 -1
  9. data/lib/rubocop/cop/rspec/be_empty.rb +1 -0
  10. data/lib/rubocop/cop/rspec/be_eq.rb +1 -1
  11. data/lib/rubocop/cop/rspec/be_eql.rb +1 -1
  12. data/lib/rubocop/cop/rspec/be_nil.rb +2 -2
  13. data/lib/rubocop/cop/rspec/before_after_all.rb +7 -13
  14. data/lib/rubocop/cop/rspec/change_by_zero.rb +30 -4
  15. data/lib/rubocop/cop/rspec/context_method.rb +2 -2
  16. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  17. data/lib/rubocop/cop/rspec/describe_symbol.rb +1 -1
  18. data/lib/rubocop/cop/rspec/described_class.rb +33 -11
  19. data/lib/rubocop/cop/rspec/dialect.rb +13 -0
  20. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +1 -1
  21. data/lib/rubocop/cop/rspec/empty_example_group.rb +4 -1
  22. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  23. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +2 -2
  24. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  25. data/lib/rubocop/cop/rspec/empty_output.rb +47 -0
  26. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  27. data/lib/rubocop/cop/rspec/example_length.rb +11 -5
  28. data/lib/rubocop/cop/rspec/example_without_description.rb +11 -2
  29. data/lib/rubocop/cop/rspec/example_wording.rb +11 -2
  30. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +14 -5
  31. data/lib/rubocop/cop/rspec/expect_actual.rb +17 -14
  32. data/lib/rubocop/cop/rspec/expect_change.rb +2 -2
  33. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  34. data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
  35. data/lib/rubocop/cop/rspec/expect_output.rb +1 -4
  36. data/lib/rubocop/cop/rspec/focus.rb +17 -2
  37. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -2
  38. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -1
  39. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +2 -2
  40. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  41. data/lib/rubocop/cop/rspec/implicit_subject.rb +2 -2
  42. data/lib/rubocop/cop/rspec/indexed_let.rb +32 -1
  43. data/lib/rubocop/cop/rspec/instance_spy.rb +2 -2
  44. data/lib/rubocop/cop/rspec/instance_variable.rb +4 -4
  45. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  46. data/lib/rubocop/cop/rspec/iterated_expectation.rb +3 -3
  47. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +2 -2
  48. data/lib/rubocop/cop/rspec/let_before_examples.rb +5 -1
  49. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  50. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -2
  51. data/lib/rubocop/cop/rspec/message_spies.rb +0 -2
  52. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  53. data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
  54. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  55. data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
  56. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +2 -2
  57. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
  58. data/lib/rubocop/cop/rspec/multiple_expectations.rb +16 -11
  59. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +2 -4
  60. data/lib/rubocop/cop/rspec/named_subject.rb +6 -3
  61. data/lib/rubocop/cop/rspec/pending.rb +12 -2
  62. data/lib/rubocop/cop/rspec/pending_without_reason.rb +1 -1
  63. data/lib/rubocop/cop/rspec/predicate_matcher.rb +10 -10
  64. data/lib/rubocop/cop/rspec/receive_counts.rb +1 -1
  65. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  66. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  67. data/lib/rubocop/cop/rspec/remove_const.rb +39 -0
  68. data/lib/rubocop/cop/rspec/repeated_example.rb +6 -6
  69. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +1 -1
  70. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +2 -2
  71. data/lib/rubocop/cop/rspec/repeated_include_example.rb +1 -1
  72. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +125 -0
  73. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  74. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  75. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  76. data/lib/rubocop/cop/rspec/shared_examples.rb +66 -20
  77. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +2 -3
  78. data/lib/rubocop/cop/rspec/sort_metadata.rb +3 -2
  79. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  80. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  81. data/lib/rubocop/cop/rspec/stubbed_mock.rb +4 -2
  82. data/lib/rubocop/cop/rspec/subject_stub.rb +6 -6
  83. data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +69 -0
  84. data/lib/rubocop/cop/rspec/unspecified_exception.rb +2 -2
  85. data/lib/rubocop/cop/rspec/variable_definition.rb +4 -4
  86. data/lib/rubocop/cop/rspec/verified_double_reference.rb +6 -6
  87. data/lib/rubocop/cop/rspec/verified_doubles.rb +2 -2
  88. data/lib/rubocop/cop/rspec/void_expect.rb +4 -3
  89. data/lib/rubocop/cop/rspec_cops.rb +14 -28
  90. data/lib/rubocop/rspec/concept.rb +0 -1
  91. data/lib/rubocop/rspec/config_formatter.rb +1 -11
  92. data/lib/rubocop/rspec/cop/generator.rb +25 -0
  93. data/lib/rubocop/rspec/language.rb +8 -9
  94. data/lib/rubocop/rspec/node.rb +1 -1
  95. data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +1 -1
  96. data/lib/rubocop/rspec/version.rb +1 -1
  97. data/lib/rubocop/rspec/wording.rb +8 -0
  98. data/lib/rubocop-rspec.rb +2 -16
  99. metadata +27 -49
  100. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +0 -39
  101. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +0 -104
  102. data/lib/rubocop/cop/rspec/capybara/match_style.rb +0 -38
  103. data/lib/rubocop/cop/rspec/capybara/negation_matcher.rb +0 -33
  104. data/lib/rubocop/cop/rspec/capybara/specific_actions.rb +0 -29
  105. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +0 -24
  106. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +0 -35
  107. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -36
  108. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +0 -128
  109. data/lib/rubocop/cop/rspec/factory_bot/consistent_parentheses_style.rb +0 -117
  110. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +0 -260
  111. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +0 -56
  112. data/lib/rubocop/cop/rspec/factory_bot/factory_name_style.rb +0 -74
  113. data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +0 -89
  114. data/lib/rubocop/cop/rspec/file_path.rb +0 -173
  115. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +0 -43
  116. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +0 -55
  117. data/lib/rubocop/cop/rspec/rails/http_status.rb +0 -203
  118. data/lib/rubocop/cop/rspec/rails/inferred_spec_type.rb +0 -145
  119. data/lib/rubocop/cop/rspec/rails/minitest_assertions.rb +0 -60
  120. data/lib/rubocop/cop/rspec/rails/travel_around.rb +0 -92
  121. data/lib/rubocop/rspec/factory_bot/language.rb +0 -37
  122. data/lib/rubocop/rspec/factory_bot.rb +0 -64
  123. data/lib/rubocop/rspec/language/node_pattern.rb +0 -48
@@ -21,3 +21,27 @@ renamed:
21
21
  RSpec/Capybara/SpecificFinders: Capybara/SpecificFinders
22
22
  RSpec/Capybara/SpecificMatcher: Capybara/SpecificMatcher
23
23
  RSpec/Capybara/VisibilityMatcher: Capybara/VisibilityMatcher
24
+ RSpec/FactoryBot/AttributeDefinedStatically: FactoryBot/AttributeDefinedStatically
25
+ RSpec/FactoryBot/ConsistentParenthesesStyle: FactoryBot/ConsistentParenthesesStyle
26
+ RSpec/FactoryBot/CreateList: FactoryBot/CreateList
27
+ RSpec/FactoryBot/FactoryClassName: FactoryBot/FactoryClassName
28
+ RSpec/FactoryBot/FactoryNameStyle: FactoryBot/FactoryNameStyle
29
+ RSpec/FactoryBot/SyntaxMethods: FactoryBot/SyntaxMethods
30
+ RSpec/Rails/AvoidSetupHook: RSpecRails/AvoidSetupHook
31
+ RSpec/Rails/HaveHttpStatus: RSpecRails/HaveHttpStatus
32
+ RSpec/Rails/HttpStatus: RSpecRails/HttpStatus
33
+ RSpec/Rails/InferredSpecType: RSpecRails/InferredSpecType
34
+ RSpec/Rails/MinitestAssertions: RSpecRails/MinitestAssertions
35
+ RSpec/Rails/NegationBeValid: RSpecRails/NegationBeValid
36
+ RSpec/Rails/TravelAround: RSpecRails/TravelAround
37
+
38
+ split:
39
+ RSpec/FilePath:
40
+ alternatives:
41
+ - RSpec/SpecFilePathFormat
42
+ - RSpec/SpecFilePathSuffix
43
+
44
+ removed:
45
+ RSpec/Capybara/FeatureMethods:
46
+ reason: >
47
+ this cop has migrated to `RSpec/Dialect`. Please use `RSpec/Dialect` instead
@@ -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
 
@@ -6,7 +6,6 @@ module RuboCop
6
6
  # @abstract parent class to RSpec cops
7
7
  class Base < ::RuboCop::Cop::Base
8
8
  include RuboCop::RSpec::Language
9
- extend RuboCop::RSpec::Language::NodePattern
10
9
 
11
10
  exclude_from_registry
12
11
 
@@ -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)
@@ -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?
@@ -29,6 +29,19 @@ module RuboCop
29
29
  # PreferredMethods:
30
30
  # context: describe
31
31
  #
32
+ # If you were previously using the `RSpec/Capybara/FeatureMethods` cop and
33
+ # want to keep disabling all Capybara-specific methods that have the same
34
+ # native RSpec method (e.g. are just aliases), use the following config:
35
+ #
36
+ # RSpec/Dialect:
37
+ # PreferredMethods:
38
+ # background: :before
39
+ # scenario: :it
40
+ # xscenario: :xit
41
+ # given: :let
42
+ # given!: :let!
43
+ # feature: :describe
44
+ #
32
45
  # You can expect the following behavior:
33
46
  #
34
47
  # @example
@@ -19,7 +19,7 @@ module RuboCop
19
19
 
20
20
  MSG = 'Avoid duplicated metadata.'
21
21
 
22
- def on_metadata(symbols, _pairs)
22
+ def on_metadata(symbols, _hash)
23
23
  symbols.each do |symbol|
24
24
  on_metadata_symbol(symbol)
25
25
  end
@@ -131,6 +131,7 @@ module RuboCop
131
131
  {
132
132
  #examples_directly_or_in_block?
133
133
  (begin <#examples_directly_or_in_block? ...>)
134
+ (begin <#examples_in_branches? ...>)
134
135
  }
135
136
  PATTERN
136
137
 
@@ -161,7 +162,7 @@ module RuboCop
161
162
  end
162
163
 
163
164
  def conditionals_with_examples?(body)
164
- return unless body.begin_type? || body.case_type?
165
+ return false unless body.begin_type? || body.case_type?
165
166
 
166
167
  body.each_descendant(:if, :case).any? do |condition_node|
167
168
  examples_in_branches?(condition_node)
@@ -169,6 +170,8 @@ module RuboCop
169
170
  end
170
171
 
171
172
  def examples_in_branches?(condition_node)
173
+ return false if !condition_node.if_type? && !condition_node.case_type?
174
+
172
175
  condition_node.branches.any? { |branch| examples?(branch) }
173
176
  end
174
177
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Checks for empty before and after hooks.
6
+ # Checks for empty before and after hooks.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -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
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Avoid empty metadata hash.
7
+ #
8
+ # @example EnforcedStyle: symbol (default)
9
+ # # bad
10
+ # describe 'Something', {}
11
+ #
12
+ # # good
13
+ # describe 'Something'
14
+ class EmptyMetadata < Base
15
+ extend AutoCorrector
16
+
17
+ include Metadata
18
+ include RangeHelp
19
+
20
+ MSG = 'Avoid empty metadata hash.'
21
+
22
+ def on_metadata(_symbols, hash)
23
+ return unless hash&.pairs&.empty?
24
+
25
+ add_offense(hash) do |corrector|
26
+ remove_empty_metadata(corrector, hash)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def remove_empty_metadata(corrector, node)
33
+ corrector.remove(
34
+ range_with_surrounding_comma(
35
+ range_with_surrounding_space(
36
+ node.source_range,
37
+ side: :left
38
+ ),
39
+ :left
40
+ )
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Check that the `output` matcher is not called with an empty string.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect { foo }.to output('').to_stdout
11
+ # expect { bar }.not_to output('').to_stderr
12
+ #
13
+ # # good
14
+ # expect { foo }.not_to output.to_stdout
15
+ # expect { bar }.to output.to_stderr
16
+ #
17
+ class EmptyOutput < Base
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `%<runner>s` instead of matching on an empty output.'
21
+ RESTRICT_ON_SEND = Runners.all
22
+
23
+ # @!method matching_empty_output(node)
24
+ def_node_matcher :matching_empty_output, <<~PATTERN
25
+ (send
26
+ (block
27
+ (send nil? :expect) ...
28
+ )
29
+ #Runners.all
30
+ (send $(send nil? :output (str empty?)) ...)
31
+ )
32
+ PATTERN
33
+
34
+ def on_send(send_node)
35
+ matching_empty_output(send_node) do |node|
36
+ runner = send_node.method?(:to) ? 'not_to' : 'to'
37
+ message = format(MSG, runner: runner)
38
+ add_offense(node, message: message) do |corrector|
39
+ corrector.replace(send_node.loc.selector, runner)
40
+ corrector.replace(node, 'output')
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Use `eq` instead of `be ==` to compare objects.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # expect(foo).to be == 42
11
+ #
12
+ # # good
13
+ # expect(foo).to eq 42
14
+ #
15
+ class Eq < Base
16
+ extend AutoCorrector
17
+ include RangeHelp
18
+
19
+ MSG = 'Use `eq` instead of `be ==` to compare objects.'
20
+ RESTRICT_ON_SEND = Runners.all
21
+
22
+ # @!method be_equals(node)
23
+ def_node_matcher :be_equals, <<~PATTERN
24
+ (send _ #Runners.all $(send (send nil? :be) :== _))
25
+ PATTERN
26
+
27
+ def on_send(node)
28
+ be_equals(node) do |matcher|
29
+ range = offense_range(matcher)
30
+ add_offense(range) do |corrector|
31
+ corrector.replace(range, 'eq')
32
+ end
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def offense_range(matcher)
39
+ range_between(
40
+ matcher.source_range.begin_pos,
41
+ matcher.loc.selector.end_pos
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -26,11 +26,12 @@ module RuboCop
26
26
  # expect(result).to be(true)
27
27
  # end
28
28
  #
29
- # You can set literals you want to fold with `CountAsOne`.
30
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
31
- # will be counted as one line regardless of its actual size.
29
+ # You can set constructs you want to fold with `CountAsOne`.
30
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'.
31
+ # Each construct will be counted as one line regardless of
32
+ # its actual size.
32
33
  #
33
- # @example CountAsOne: ['array', 'heredoc']
34
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
34
35
  #
35
36
  # it do
36
37
  # array = [ # +1
@@ -46,7 +47,12 @@ module RuboCop
46
47
  # Heredoc
47
48
  # content.
48
49
  # HEREDOC
49
- # end # 5 points
50
+ #
51
+ # foo( # +1
52
+ # 1,
53
+ # 2
54
+ # )
55
+ # end # 6 points
50
56
  #
51
57
  class ExampleLength < Base
52
58
  include CodeLength
@@ -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