rubocop-rspec 3.9.0 → 3.10.2

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/config/default.yml +18 -1
  4. data/lib/rubocop/cop/rspec/around_block.rb +22 -0
  5. data/lib/rubocop/cop/rspec/contain_exactly.rb +8 -22
  6. data/lib/rubocop/cop/rspec/context_method.rb +1 -1
  7. data/lib/rubocop/cop/rspec/context_wording.rb +1 -1
  8. data/lib/rubocop/cop/rspec/described_class.rb +1 -1
  9. data/lib/rubocop/cop/rspec/discarded_matcher.rb +113 -0
  10. data/lib/rubocop/cop/rspec/empty_example_group.rb +3 -2
  11. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  12. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
  13. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  14. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +7 -2
  15. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -0
  16. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +1 -1
  17. data/lib/rubocop/cop/rspec/example_length.rb +1 -1
  18. data/lib/rubocop/cop/rspec/example_without_description.rb +1 -1
  19. data/lib/rubocop/cop/rspec/example_wording.rb +1 -1
  20. data/lib/rubocop/cop/rspec/expect_actual.rb +33 -13
  21. data/lib/rubocop/cop/rspec/expect_change.rb +46 -16
  22. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -0
  23. data/lib/rubocop/cop/rspec/expect_in_let.rb +1 -1
  24. data/lib/rubocop/cop/rspec/hook_argument.rb +1 -0
  25. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +1 -0
  26. data/lib/rubocop/cop/rspec/indexed_let.rb +1 -1
  27. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  28. data/lib/rubocop/cop/rspec/iterated_expectation.rb +13 -0
  29. data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
  30. data/lib/rubocop/cop/rspec/let_before_examples.rb +1 -1
  31. data/lib/rubocop/cop/rspec/let_setup.rb +1 -1
  32. data/lib/rubocop/cop/rspec/match_with_simple_regex.rb +92 -0
  33. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +1 -1
  34. data/lib/rubocop/cop/rspec/mixin/inside_example.rb +16 -0
  35. data/lib/rubocop/cop/rspec/mixin/metadata.rb +1 -0
  36. data/lib/rubocop/cop/rspec/mixin/repeated_items.rb +36 -0
  37. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  38. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -1
  39. data/lib/rubocop/cop/rspec/multiple_subjects.rb +1 -1
  40. data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
  41. data/lib/rubocop/cop/rspec/no_expectation_example.rb +1 -0
  42. data/lib/rubocop/cop/rspec/overwriting_setup.rb +1 -1
  43. data/lib/rubocop/cop/rspec/predicate_matcher.rb +1 -1
  44. data/lib/rubocop/cop/rspec/redundant_around.rb +1 -0
  45. data/lib/rubocop/cop/rspec/repeated_description.rb +1 -1
  46. data/lib/rubocop/cop/rspec/repeated_example.rb +7 -5
  47. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +6 -10
  48. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +6 -10
  49. data/lib/rubocop/cop/rspec/repeated_include_example.rb +8 -11
  50. data/lib/rubocop/cop/rspec/return_from_stub.rb +1 -1
  51. data/lib/rubocop/cop/rspec/scattered_let.rb +11 -5
  52. data/lib/rubocop/cop/rspec/scattered_setup.rb +7 -9
  53. data/lib/rubocop/cop/rspec/shared_context.rb +47 -7
  54. data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +3 -6
  55. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +20 -12
  56. data/lib/rubocop/cop/rspec/subject_declaration.rb +17 -1
  57. data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +1 -1
  58. data/lib/rubocop/cop/rspec/void_expect.rb +3 -5
  59. data/lib/rubocop/cop/rspec/yield.rb +1 -1
  60. data/lib/rubocop/cop/rspec_cops.rb +2 -0
  61. data/lib/rubocop/rspec/description_extractor.rb +1 -1
  62. data/lib/rubocop/rspec/version.rb +1 -1
  63. data/lib/rubocop-rspec.rb +2 -0
  64. metadata +28 -4
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # If there are no examples defined, use shared_context.
9
9
  # If there is no setup defined, use shared_examples.
10
10
  #
11
+ # With `Strict: true`, `shared_context` is flagged whenever it contains
12
+ # any examples, even if it also contains setup code.
13
+ #
11
14
  # @example
12
15
  # # bad
13
16
  # RSpec.shared_context 'only examples here' do
@@ -50,11 +53,31 @@ module RuboCop
50
53
  # end
51
54
  # end
52
55
  #
56
+ # @example Strict: true
57
+ # # bad - shared_context with examples is flagged
58
+ # RSpec.shared_context 'setup and examples' do
59
+ # let(:foo) { :bar }
60
+ #
61
+ # it 'does x' do
62
+ # end
63
+ # end
64
+ #
65
+ # # good - split into separate shared_context and shared_examples
66
+ # RSpec.shared_context 'setup' do
67
+ # let(:foo) { :bar }
68
+ # end
69
+ #
70
+ # RSpec.shared_examples 'examples' do
71
+ # it 'does x' do
72
+ # end
73
+ # end
74
+ #
53
75
  class SharedContext < Base
54
76
  extend AutoCorrector
55
77
 
56
78
  MSG_EXAMPLES = "Use `shared_examples` when you don't define context."
57
- MSG_CONTEXT = "Use `shared_context` when you don't define examples."
79
+ MSG_EXAMPLES_STRICT = 'Use `shared_examples` when you define examples.'
80
+ MSG_CONTEXT = "Use `shared_context` when you don't define examples."
58
81
 
59
82
  # @!method examples?(node)
60
83
  def_node_search :examples?, <<~PATTERN
@@ -78,10 +101,13 @@ module RuboCop
78
101
  (block (send #rspec? #SharedGroups.examples ...) ...)
79
102
  PATTERN
80
103
 
81
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
82
- context_with_only_examples(node) do
83
- add_offense(node.send_node, message: MSG_EXAMPLES) do |corrector|
84
- corrector.replace(node.send_node.loc.selector, 'shared_examples')
104
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
105
+ offending_node(node) do
106
+ add_offense(node.send_node, message: message) do |corrector|
107
+ if can_correct?(node)
108
+ corrector.replace(node.send_node.loc.selector,
109
+ 'shared_examples')
110
+ end
85
111
  end
86
112
  end
87
113
 
@@ -94,8 +120,22 @@ module RuboCop
94
120
 
95
121
  private
96
122
 
97
- def context_with_only_examples(node)
98
- shared_context(node) { yield if examples?(node) && !context?(node) }
123
+ def strict?
124
+ cop_config.fetch('Strict', false)
125
+ end
126
+
127
+ def message
128
+ strict? ? MSG_EXAMPLES_STRICT : MSG_EXAMPLES
129
+ end
130
+
131
+ def can_correct?(node)
132
+ !strict? || !context?(node)
133
+ end
134
+
135
+ def offending_node(node)
136
+ shared_context(node) do
137
+ yield if examples?(node) && (strict? || !context?(node))
138
+ end
99
139
  end
100
140
 
101
141
  def examples_with_only_context(node)
@@ -24,6 +24,8 @@ module RuboCop
24
24
  # end
25
25
  #
26
26
  class SkipBlockInsideExample < Base
27
+ include InsideExample
28
+
27
29
  MSG = "Don't pass a block to `skip` inside examples."
28
30
 
29
31
  def on_block(node)
@@ -34,12 +36,7 @@ module RuboCop
34
36
  end
35
37
 
36
38
  alias on_numblock on_block
37
-
38
- private
39
-
40
- def inside_example?(node)
41
- node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
42
- end
39
+ alias on_itblock on_block
43
40
  end
44
41
  end
45
42
  end
@@ -44,6 +44,7 @@ module RuboCop
44
44
  include FileHelp
45
45
 
46
46
  MSG = 'Spec path should end with `%<suffix>s`.'
47
+ PATH_NAME_BOUNDARY = '(?![[:alnum:]])'
47
48
 
48
49
  # @!method example_group_arguments(node)
49
50
  def_node_matcher :example_group_arguments, <<~PATTERN
@@ -119,7 +120,11 @@ module RuboCop
119
120
  # For the suffix shown in the offense message, modify the regular
120
121
  # expression pattern to resemble a glob pattern for clearer error
121
122
  # messages.
122
- suffix = pattern.sub('.*', '*').sub('[^/]*', '*').sub('\.', '.')
123
+ suffix = pattern
124
+ .sub(PATH_NAME_BOUNDARY, '')
125
+ .sub('.*', '*')
126
+ .sub('[^/]*', '*')
127
+ .sub('\.', '.')
123
128
  add_offense(send_node, message: format(MSG, suffix: suffix))
124
129
  end
125
130
 
@@ -132,19 +137,21 @@ module RuboCop
132
137
  end
133
138
 
134
139
  def correct_path_pattern(class_name, arguments)
135
- path = [expected_path(class_name)]
136
- path << '.*' unless ignore?(arguments.first)
137
- path << [name_pattern(arguments.first), '[^/]*_spec\.rb']
138
- path.join
140
+ [
141
+ expected_path(class_name),
142
+ PATH_NAME_BOUNDARY,
143
+ method_name_pattern(arguments.first),
144
+ '[^/]*_spec\.rb'
145
+ ].join
139
146
  end
140
147
 
141
- def name_pattern(method_name)
142
- return if ignore?(method_name)
148
+ def method_name_pattern(method_name)
149
+ return if ignore_method_name?(method_name)
143
150
 
144
- method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')
151
+ ".*#{method_name.str_content.gsub(/\s/, '_').gsub(/\W/, '')}"
145
152
  end
146
153
 
147
- def ignore?(method_name)
154
+ def ignore_method_name?(method_name)
148
155
  !method_name&.str_type? || ignore_methods?
149
156
  end
150
157
 
@@ -152,8 +159,9 @@ module RuboCop
152
159
  constants = namespace(constant) + constant.const_name.split('::')
153
160
 
154
161
  File.join(
155
- constants.map do |name|
156
- custom_transform.fetch(name) { camel_to_snake_case(name) }
162
+ constants.filter_map do |name|
163
+ path = custom_transform.fetch(name) { camel_to_snake_case(name) }
164
+ path unless path.empty?
157
165
  end
158
166
  )
159
167
  end
@@ -175,7 +183,7 @@ module RuboCop
175
183
  end
176
184
 
177
185
  def filename_ends_with?(pattern)
178
- expanded_file_path.match?("#{pattern}$")
186
+ expanded_file_path.match?(%r{(?:\A|/)#{pattern}$})
179
187
  end
180
188
  end
181
189
  end
@@ -20,6 +20,8 @@ module RuboCop
20
20
  # subject(:test_subject) { foo }
21
21
  #
22
22
  class SubjectDeclaration < Base
23
+ extend AutoCorrector
24
+
23
25
  MSG_LET = 'Use subject explicitly rather than using let'
24
26
  MSG_REDUNDANT = 'Ambiguous declaration of subject'
25
27
 
@@ -32,7 +34,11 @@ module RuboCop
32
34
  offense = offensive_subject_declaration?(node)
33
35
  return unless offense
34
36
 
35
- add_offense(node, message: message_for(offense))
37
+ add_offense(node, message: message_for(offense)) do |corrector|
38
+ corrector.replace(node.loc.selector,
39
+ node.method_name.to_s.sub('let', 'subject'))
40
+ corrector.remove(first_argument_range(node))
41
+ end
36
42
  end
37
43
 
38
44
  private
@@ -40,6 +46,16 @@ module RuboCop
40
46
  def message_for(offense)
41
47
  Helpers.all(offense) ? MSG_LET : MSG_REDUNDANT
42
48
  end
49
+
50
+ def first_argument_range(node)
51
+ if node.arguments.one?
52
+ node.loc.begin.join(node.loc.end)
53
+ else
54
+ node.first_argument.source_range.with(
55
+ end_pos: node.arguments[1].source_range.begin_pos
56
+ )
57
+ end
58
+ end
43
59
  end
44
60
  end
45
61
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  (block (send #rspec? {#ExampleGroups.all #Examples.all} $_) ...)
53
53
  PATTERN
54
54
 
55
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
55
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
56
56
  example_groups_or_example?(node) do |arg|
57
57
  add_offense(arg) if offense?(arg)
58
58
  end
@@ -13,6 +13,8 @@ module RuboCop
13
13
  # expect(something).to be(1)
14
14
  #
15
15
  class VoidExpect < Base
16
+ include InsideExample
17
+
16
18
  MSG = 'Do not use `expect()` without `.to` or `.not_to`. ' \
17
19
  'Chain the methods or remove it.'
18
20
  RESTRICT_ON_SEND = %i[expect].freeze
@@ -34,7 +36,7 @@ module RuboCop
34
36
  check_expect(node)
35
37
  end
36
38
 
37
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
39
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
38
40
  return unless expect_block?(node)
39
41
  return unless inside_example?(node)
40
42
 
@@ -55,10 +57,6 @@ module RuboCop
55
57
 
56
58
  parent.block_type? && parent.body == expect
57
59
  end
58
-
59
- def inside_example?(node)
60
- node.each_ancestor(:block).any? { |ancestor| example?(ancestor) }
61
- end
62
60
  end
63
61
  end
64
62
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  # @!method block_call?(node)
28
28
  def_node_matcher :block_call?, '(send (lvar %) :call ...)'
29
29
 
30
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
30
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
31
31
  return unless method_on_stub?(node.send_node)
32
32
 
33
33
  block_arg(node.arguments) do |block|
@@ -21,6 +21,7 @@ require_relative 'rspec/describe_symbol'
21
21
  require_relative 'rspec/described_class'
22
22
  require_relative 'rspec/described_class_module_wrapping'
23
23
  require_relative 'rspec/dialect'
24
+ require_relative 'rspec/discarded_matcher'
24
25
  require_relative 'rspec/duplicated_metadata'
25
26
  require_relative 'rspec/empty_example_group'
26
27
  require_relative 'rspec/empty_hook'
@@ -61,6 +62,7 @@ require_relative 'rspec/leaky_local_variable'
61
62
  require_relative 'rspec/let_before_examples'
62
63
  require_relative 'rspec/let_setup'
63
64
  require_relative 'rspec/match_array'
65
+ require_relative 'rspec/match_with_simple_regex'
64
66
  require_relative 'rspec/message_chain'
65
67
  require_relative 'rspec/message_expectation'
66
68
  require_relative 'rspec/message_spies'
@@ -50,7 +50,7 @@ module RuboCop
50
50
  end
51
51
 
52
52
  def description
53
- yardoc.docstring.split("\n\n").first.to_s
53
+ yardoc.docstring.split("\n\n").first.to_s.tr("\n", ' ')
54
54
  end
55
55
 
56
56
  def rspec_cop_namespace?
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '3.9.0'
7
+ STRING = '3.10.2'
8
8
  end
9
9
  end
10
10
  end
data/lib/rubocop-rspec.rb CHANGED
@@ -14,10 +14,12 @@ require_relative 'rubocop/rspec/wording'
14
14
 
15
15
  require_relative 'rubocop/cop/rspec/mixin/file_help'
16
16
  require_relative 'rubocop/cop/rspec/mixin/final_end_location'
17
+ require_relative 'rubocop/cop/rspec/mixin/inside_example'
17
18
  require_relative 'rubocop/cop/rspec/mixin/inside_example_group'
18
19
  require_relative 'rubocop/cop/rspec/mixin/location_help'
19
20
  require_relative 'rubocop/cop/rspec/mixin/metadata'
20
21
  require_relative 'rubocop/cop/rspec/mixin/namespace'
22
+ require_relative 'rubocop/cop/rspec/mixin/repeated_items'
21
23
  require_relative 'rubocop/cop/rspec/mixin/skip_or_pending'
22
24
  require_relative 'rubocop/cop/rspec/mixin/top_level_group'
23
25
  require_relative 'rubocop/cop/rspec/mixin/variable'
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: 3.9.0
4
+ version: 3.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Backus
@@ -25,20 +25,40 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.1'
28
+ - !ruby/object:Gem::Dependency
29
+ name: regexp_parser
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '2.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '2.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rubocop
30
44
  requirement: !ruby/object:Gem::Requirement
31
45
  requirements:
32
46
  - - "~>"
33
47
  - !ruby/object:Gem::Version
34
- version: '1.81'
48
+ version: '1.86'
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: 1.86.2
35
52
  type: :runtime
36
53
  prerelease: false
37
54
  version_requirements: !ruby/object:Gem::Requirement
38
55
  requirements:
39
56
  - - "~>"
40
57
  - !ruby/object:Gem::Version
41
- version: '1.81'
58
+ version: '1.86'
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: 1.86.2
42
62
  description: |
43
63
  Code style checking for RSpec files.
44
64
  A plugin for the RuboCop code style enforcing & linting tool.
@@ -81,6 +101,7 @@ files:
81
101
  - lib/rubocop/cop/rspec/described_class.rb
82
102
  - lib/rubocop/cop/rspec/described_class_module_wrapping.rb
83
103
  - lib/rubocop/cop/rspec/dialect.rb
104
+ - lib/rubocop/cop/rspec/discarded_matcher.rb
84
105
  - lib/rubocop/cop/rspec/duplicated_metadata.rb
85
106
  - lib/rubocop/cop/rspec/empty_example_group.rb
86
107
  - lib/rubocop/cop/rspec/empty_hook.rb
@@ -121,6 +142,7 @@ files:
121
142
  - lib/rubocop/cop/rspec/let_before_examples.rb
122
143
  - lib/rubocop/cop/rspec/let_setup.rb
123
144
  - lib/rubocop/cop/rspec/match_array.rb
145
+ - lib/rubocop/cop/rspec/match_with_simple_regex.rb
124
146
  - lib/rubocop/cop/rspec/message_chain.rb
125
147
  - lib/rubocop/cop/rspec/message_expectation.rb
126
148
  - lib/rubocop/cop/rspec/message_spies.rb
@@ -131,10 +153,12 @@ files:
131
153
  - lib/rubocop/cop/rspec/mixin/empty_line_separation.rb
132
154
  - lib/rubocop/cop/rspec/mixin/file_help.rb
133
155
  - lib/rubocop/cop/rspec/mixin/final_end_location.rb
156
+ - lib/rubocop/cop/rspec/mixin/inside_example.rb
134
157
  - lib/rubocop/cop/rspec/mixin/inside_example_group.rb
135
158
  - lib/rubocop/cop/rspec/mixin/location_help.rb
136
159
  - lib/rubocop/cop/rspec/mixin/metadata.rb
137
160
  - lib/rubocop/cop/rspec/mixin/namespace.rb
161
+ - lib/rubocop/cop/rspec/mixin/repeated_items.rb
138
162
  - lib/rubocop/cop/rspec/mixin/skip_or_pending.rb
139
163
  - lib/rubocop/cop/rspec/mixin/top_level_group.rb
140
164
  - lib/rubocop/cop/rspec/mixin/variable.rb
@@ -223,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
247
  - !ruby/object:Gem::Version
224
248
  version: '0'
225
249
  requirements: []
226
- rubygems_version: 4.0.3
250
+ rubygems_version: 4.0.10
227
251
  specification_version: 4
228
252
  summary: Code style checking for RSpec files
229
253
  test_files: []