rubocop-rspec 1.12.0 → 1.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/config/default.yml +15 -1
  4. data/lib/rubocop-rspec.rb +2 -0
  5. data/lib/rubocop/cop/rspec/any_instance.rb +7 -10
  6. data/lib/rubocop/cop/rspec/around_block.rb +19 -30
  7. data/lib/rubocop/cop/rspec/be_eql.rb +3 -7
  8. data/lib/rubocop/cop/rspec/before_after_all.rb +10 -16
  9. data/lib/rubocop/cop/rspec/cop.rb +5 -1
  10. data/lib/rubocop/cop/rspec/describe_class.rb +8 -8
  11. data/lib/rubocop/cop/rspec/describe_method.rb +6 -5
  12. data/lib/rubocop/cop/rspec/described_class.rb +2 -2
  13. data/lib/rubocop/cop/rspec/example_length.rb +5 -8
  14. data/lib/rubocop/cop/rspec/example_wording.rb +57 -23
  15. data/lib/rubocop/cop/rspec/expect_actual.rb +3 -9
  16. data/lib/rubocop/cop/rspec/expect_output.rb +2 -2
  17. data/lib/rubocop/cop/rspec/file_path.rb +30 -29
  18. data/lib/rubocop/cop/rspec/hook_argument.rb +1 -1
  19. data/lib/rubocop/cop/rspec/instance_spy.rb +12 -12
  20. data/lib/rubocop/cop/rspec/instance_variable.rb +2 -2
  21. data/lib/rubocop/cop/rspec/it_behaves_like.rb +47 -0
  22. data/lib/rubocop/cop/rspec/leading_subject.rb +1 -1
  23. data/lib/rubocop/cop/rspec/message_chain.rb +7 -4
  24. data/lib/rubocop/cop/rspec/message_spies.rb +6 -5
  25. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -1
  26. data/lib/rubocop/cop/rspec/multiple_expectations.rb +38 -6
  27. data/lib/rubocop/cop/rspec/named_subject.rb +2 -2
  28. data/lib/rubocop/cop/rspec/nested_groups.rb +10 -6
  29. data/lib/rubocop/cop/rspec/not_to_not.rb +12 -23
  30. data/lib/rubocop/cop/rspec/shared_context.rb +107 -0
  31. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +16 -23
  32. data/lib/rubocop/cop/rspec/subject_stub.rb +4 -4
  33. data/lib/rubocop/cop/rspec/verified_doubles.rb +4 -3
  34. data/lib/rubocop/rspec/example_group.rb +1 -1
  35. data/lib/rubocop/rspec/language.rb +25 -7
  36. data/lib/rubocop/rspec/version.rb +1 -1
  37. data/spec/expect_violation/expectation_spec.rb +16 -16
  38. data/spec/project/changelog_spec.rb +1 -1
  39. data/spec/project/default_config_spec.rb +1 -1
  40. data/spec/project/project_requires_spec.rb +1 -1
  41. data/spec/rubocop/cop/rspec/any_instance_spec.rb +4 -4
  42. data/spec/rubocop/cop/rspec/around_block_spec.rb +115 -26
  43. data/spec/rubocop/cop/rspec/be_eql_spec.rb +9 -9
  44. data/spec/rubocop/cop/rspec/before_after_all_spec.rb +38 -80
  45. data/spec/rubocop/cop/rspec/describe_class_spec.rb +1 -1
  46. data/spec/rubocop/cop/rspec/describe_method_spec.rb +2 -2
  47. data/spec/rubocop/cop/rspec/described_class_spec.rb +13 -13
  48. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +1 -1
  49. data/spec/rubocop/cop/rspec/example_length_spec.rb +3 -32
  50. data/spec/rubocop/cop/rspec/example_wording_spec.rb +21 -2
  51. data/spec/rubocop/cop/rspec/expect_actual_spec.rb +33 -18
  52. data/spec/rubocop/cop/rspec/expect_output_spec.rb +3 -3
  53. data/spec/rubocop/cop/rspec/file_path_spec.rb +119 -170
  54. data/spec/rubocop/cop/rspec/focus_spec.rb +1 -1
  55. data/spec/rubocop/cop/rspec/hook_argument_spec.rb +1 -3
  56. data/spec/rubocop/cop/rspec/implicit_expect_spec.rb +1 -1
  57. data/spec/rubocop/cop/rspec/instance_spy_spec.rb +11 -11
  58. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +4 -4
  59. data/spec/rubocop/cop/rspec/it_behaves_like_spec.rb +51 -0
  60. data/spec/rubocop/cop/rspec/leading_subject_spec.rb +1 -1
  61. data/spec/rubocop/cop/rspec/let_setup_spec.rb +1 -1
  62. data/spec/rubocop/cop/rspec/message_chain_spec.rb +3 -3
  63. data/spec/rubocop/cop/rspec/message_expectation_spec.rb +5 -23
  64. data/spec/rubocop/cop/rspec/message_spies_spec.rb +9 -23
  65. data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +1 -1
  66. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +66 -3
  67. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +4 -4
  68. data/spec/rubocop/cop/rspec/not_to_not_spec.rb +3 -3
  69. data/spec/rubocop/cop/rspec/repeated_description_spec.rb +1 -1
  70. data/spec/rubocop/cop/rspec/repeated_example_spec.rb +1 -1
  71. data/spec/rubocop/cop/rspec/scattered_setup_spec.rb +1 -1
  72. data/spec/rubocop/cop/rspec/shared_context_spec.rb +142 -0
  73. data/spec/rubocop/cop/rspec/single_argument_message_chain_spec.rb +5 -5
  74. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +1 -1
  75. data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +2 -2
  76. data/spec/rubocop/rspec/config_formatter_spec.rb +12 -12
  77. data/spec/rubocop/rspec/description_extractor_spec.rb +23 -23
  78. data/spec/rubocop/rspec/example_group_spec.rb +11 -11
  79. data/spec/rubocop/rspec/example_spec.rb +1 -1
  80. data/spec/rubocop/rspec/language/selector_set_spec.rb +1 -1
  81. data/spec/rubocop/rspec/util/one_spec.rb +1 -1
  82. data/spec/rubocop/rspec/wording_spec.rb +1 -1
  83. data/spec/shared/detects_style_behavior.rb +3 -4
  84. data/spec/spec_helper.rb +10 -0
  85. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 48bfb61579b01b4044eeca76aa125c026ac18630
4
- data.tar.gz: 240d2ce3b483174eae7c4049fcc65f8301bf48a1
3
+ metadata.gz: 94ad662f54f32cf6cbde51f8c2fdd788fa44e989
4
+ data.tar.gz: d0c3723b84a23c6589aa10d9f379b7d56b332bcc
5
5
  SHA512:
6
- metadata.gz: 09f351caba55c9c7ad8710e286fec4b9f087028a64a886c4edf25d69b3147ecedebd5e7eeacec52a81285e14c3cd46cf615fbfef3c15a542fe6cd6c6a9d28a0c
7
- data.tar.gz: 120152a6271b3eb853a9e8c4b19716667b401f732a712c045ebada5df7371c3a843e49d5a8a83cb5197fd7a9bbf4a12e6ef245fdf9df9cd26155a849cdcdeffc
6
+ metadata.gz: fbc83814064a1e25163689e9da12bcfcac4c8f0849c62fe34adb13b6fa7a569f8a37a627bd04fe067f28bec140c4d8f89c9b2bed537b21eaafdeac9ad32c8663
7
+ data.tar.gz: cf26853505de97b2ba385add43eb5cbf71cac5395c9f773208f93d4bd02b9650e023ee9a27b4c0f23e0ee7909370d8c8060fa05fa2299aa639ba3837054cdfb1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 1.13.0 (2017-03-07)
6
+
7
+ * Add repeated 'it' detection to `RSpec/ExampleWording` cop. ([@dgollahon][])
8
+ * Add [observed_nesting/max_nesting] info to `RSpec/NestedGroups` messages. ([@dgollahon][])
9
+ * Add `RSpec/ItBehavesLike` cop. ([@dgollahon][])
10
+ * Add `RSpec/SharedContext` cop. ([@Darhazer][])
11
+ * `Rspec/MultipleExpectations`: Count aggregate_failures block as single expectation. ([@Darhazer][])
12
+ * Fix `ExpectActual` cop flagging `rspec-rails` routing specs. ([@backus][])
13
+ * Fix `FilePath` cop not registering offenses for files like `spec/blog/user.rb` when it should be `spec/blog/user_spec.rb`. ([@backus][])
14
+
5
15
  ## 1.12.0 (2017-02-21)
6
16
 
7
17
  * Add `RSpec/InstanceSpy` cop. ([@Darhazer][])
@@ -186,3 +196,4 @@
186
196
  [@Darhazer]: https://github.com/Darhazer
187
197
  [@redross]: https://github.com/redross
188
198
  [@cfabianski]: https://github.com/cfabianski
199
+ [@dgollahon]: https://github.com/dgollahon
data/config/default.yml CHANGED
@@ -53,7 +53,7 @@ RSpec/ExampleLength:
53
53
  Max: 5
54
54
 
55
55
  RSpec/ExampleWording:
56
- Description: Checks that example descriptions do not start with "should".
56
+ Description: Checks for common mistakes in example descriptions.
57
57
  Enabled: true
58
58
  CustomTransform:
59
59
  be: is
@@ -64,6 +64,8 @@ RSpec/ExampleWording:
64
64
  RSpec/ExpectActual:
65
65
  Description: Checks for `expect(...)` calls containing literal values.
66
66
  Enabled: true
67
+ Exclude:
68
+ - spec/routing/**/*
67
69
 
68
70
  RSpec/ExpectOutput:
69
71
  Description: Checks for opportunities to use `expect { ... }.to output`.
@@ -107,6 +109,14 @@ RSpec/InstanceVariable:
107
109
  AssignmentOnly: false
108
110
  Enabled: true
109
111
 
112
+ RSpec/ItBehavesLike:
113
+ Description: Checks that only one `it_behaves_like` style is used.
114
+ Enabled: true
115
+ EnforcedStyle: it_behaves_like
116
+ SupportedStyles:
117
+ - it_behaves_like
118
+ - it_should_behave_like
119
+
110
120
  RSpec/LeadingSubject:
111
121
  Description: Checks for `subject` definitions that come after `let` definitions.
112
122
  Enabled: true
@@ -169,6 +179,10 @@ RSpec/RepeatedExample:
169
179
  Enabled: true
170
180
  Description: Check for repeated examples within example groups.
171
181
 
182
+ RSpec/SharedContext:
183
+ Description: Checks for proper shared_context and shared_examples usage.
184
+ Enabled: true
185
+
172
186
  RSpec/SingleArgumentMessageChain:
173
187
  Description: Checks that chains of messages contain more than one element.
174
188
  Enabled: true
data/lib/rubocop-rspec.rb CHANGED
@@ -38,6 +38,7 @@ require 'rubocop/cop/rspec/hook_argument'
38
38
  require 'rubocop/cop/rspec/implicit_expect'
39
39
  require 'rubocop/cop/rspec/instance_spy'
40
40
  require 'rubocop/cop/rspec/instance_variable'
41
+ require 'rubocop/cop/rspec/it_behaves_like'
41
42
  require 'rubocop/cop/rspec/leading_subject'
42
43
  require 'rubocop/cop/rspec/let_setup'
43
44
  require 'rubocop/cop/rspec/message_chain'
@@ -51,6 +52,7 @@ require 'rubocop/cop/rspec/not_to_not'
51
52
  require 'rubocop/cop/rspec/repeated_description'
52
53
  require 'rubocop/cop/rspec/repeated_example'
53
54
  require 'rubocop/cop/rspec/scattered_setup'
55
+ require 'rubocop/cop/rspec/shared_context'
54
56
  require 'rubocop/cop/rspec/single_argument_message_chain'
55
57
  require 'rubocop/cop/rspec/subject_stub'
56
58
  require 'rubocop/cop/rspec/verified_doubles'
@@ -21,19 +21,16 @@ module RuboCop
21
21
  # end
22
22
  # end
23
23
  class AnyInstance < Cop
24
- MESSAGE = 'Avoid stubbing using `%{method}`'.freeze
24
+ MSG = 'Avoid stubbing using `%{method}`.'.freeze
25
25
 
26
- ANY_INSTANCE_METHODS = [
27
- :any_instance,
28
- :allow_any_instance_of,
29
- :expect_any_instance_of
30
- ].freeze
26
+ def_node_matcher :disallowed_stub, <<-PATTERN
27
+ (send _ ${:any_instance :allow_any_instance_of :expect_any_instance_of} ...)
28
+ PATTERN
31
29
 
32
30
  def on_send(node)
33
- _receiver, method_name, *_args = *node
34
- return unless ANY_INSTANCE_METHODS.include?(method_name)
35
-
36
- add_offense(node, :expression, format(MESSAGE, method: method_name))
31
+ disallowed_stub(node) do |method|
32
+ add_offense(node, :expression, format(MSG, method: method))
33
+ end
37
34
  end
38
35
  end
39
36
  end
@@ -24,53 +24,42 @@ module RuboCop
24
24
  # test.run
25
25
  # end
26
26
  class AroundBlock < Cop
27
- MSG_NO_ARG = 'Test object should be passed to around block'.freeze
28
- MSG_UNUSED_ARG = 'You should call `%<arg>s.call` ' \
29
- 'or `%<arg>s.run`'.freeze
27
+ MSG_NO_ARG = 'Test object should be passed to around block.'.freeze
28
+ MSG_UNUSED_ARG = 'You should call `%<arg>s.call` '\
29
+ 'or `%<arg>s.run`.'.freeze
30
30
 
31
- def_node_matcher :scoped_hook, <<-PATTERN
32
- (block (send nil :around (sym {:each :example})) $(args ...) ...)
31
+ def_node_matcher :hook, <<-PATTERN
32
+ (block {(send nil :around) (send nil :around sym)} (args $...) ...)
33
33
  PATTERN
34
34
 
35
- def_node_matcher :unscoped_hook, <<-PATTERN
36
- (block (send nil :around) $(args ...) ...)
35
+ def_node_search :find_arg_usage, <<-PATTERN
36
+ {(send $... {:call :run}) (send _ _ $...) (yield $...) (block-pass $...)}
37
37
  PATTERN
38
38
 
39
- def_node_search :find_arg_usage, '(lvar $_)'
40
-
41
39
  def on_block(node)
42
- hook(node) do |parameters|
43
- missing_parameters(parameters) do
44
- add_offense(node, :expression, MSG_NO_ARG)
45
- return
46
- end
47
-
48
- unused_parameters(parameters) do |param, name|
49
- add_offense(param, :expression, format(MSG_UNUSED_ARG, arg: name))
40
+ hook(node) do |(example_proxy)|
41
+ if example_proxy.nil?
42
+ add_no_arg_offense(node)
43
+ else
44
+ check_for_unused_proxy(node, example_proxy)
50
45
  end
51
46
  end
52
47
  end
53
48
 
54
49
  private
55
50
 
56
- def missing_parameters(node)
57
- yield if node.children[0].nil?
51
+ def add_no_arg_offense(node)
52
+ add_offense(node, :expression, MSG_NO_ARG)
58
53
  end
59
54
 
60
- def unused_parameters(node)
61
- first_arg = node.children[0]
62
- param, _methods, _args = *first_arg
63
- start = node.parent
55
+ def check_for_unused_proxy(block, proxy)
56
+ name, = *proxy
64
57
 
65
- find_arg_usage(start) do |name|
66
- return if param == name
58
+ find_arg_usage(block) do |usage|
59
+ return if usage.include?(s(:lvar, name))
67
60
  end
68
61
 
69
- yield first_arg, param
70
- end
71
-
72
- def hook(node, &block)
73
- scoped_hook(node, &block) || unscoped_hook(node, &block)
62
+ add_offense(proxy, :expression, format(MSG_UNUSED_ARG, arg: name))
74
63
  end
75
64
  end
76
65
  end
@@ -34,22 +34,18 @@ module RuboCop
34
34
  # coerce objects for comparison.
35
35
  #
36
36
  class BeEql < Cop
37
- MSG = 'Prefer `be` over `eql`'.freeze
37
+ MSG = 'Prefer `be` over `eql`.'.freeze
38
38
 
39
39
  def_node_matcher :eql_type_with_identity, <<-PATTERN
40
40
  (send _ :to $(send nil :eql {true false int float sym nil_type?}))
41
41
  PATTERN
42
42
 
43
43
  def on_send(node)
44
- eql_type_with_identity(node) do |eql|
45
- add_offense(eql, :selector, MSG)
46
- end
44
+ eql_type_with_identity(node) { |eql| add_offense(eql, :selector) }
47
45
  end
48
46
 
49
47
  def autocorrect(node)
50
- lambda do |corrector|
51
- corrector.replace(node.loc.selector, 'be')
52
- end
48
+ ->(corrector) { corrector.replace(node.loc.selector, 'be') }
53
49
  end
54
50
  end
55
51
  end
@@ -24,25 +24,19 @@ module RuboCop
24
24
  # after(:each) { Widget.delete_all }
25
25
  # end
26
26
  class BeforeAfterAll < Cop
27
- MESSAGE = 'Beware of using `before/after(:all)` as it may cause state '\
28
- 'to leak between tests. If you are using rspec-rails, and '\
29
- '`use_transactional_fixtures` is enabled, then records created in '\
30
- '`before(:all)` are not rolled back.'.freeze
27
+ MSG = 'Beware of using `%<hook>s` as it may cause state to leak '\
28
+ 'between tests. If you are using `rspec-rails`, and '\
29
+ '`use_transactional_fixtures` is enabled, then records created '\
30
+ 'in `%<hook>s` are not automatically rolled back.'.freeze
31
31
 
32
- BEFORE_AFTER_METHODS = [
33
- :before,
34
- :after
35
- ].freeze
36
-
37
- ALL_PAIR = s(:sym, :all)
38
- CONTEXT_PAIR = s(:sym, :context)
32
+ def_node_matcher :before_or_after_all, <<-PATTERN
33
+ $(send _ {:before :after} (sym {:all :context}))
34
+ PATTERN
39
35
 
40
36
  def on_send(node)
41
- _receiver, method_name, *args = *node
42
- return unless BEFORE_AFTER_METHODS.include?(method_name)
43
- return unless args.include?(ALL_PAIR) || args.include?(CONTEXT_PAIR)
44
-
45
- add_offense(node, :expression, MESSAGE)
37
+ before_or_after_all(node) do |hook|
38
+ add_offense(node, :expression, format(MSG, hook: hook.source))
39
+ end
46
40
  end
47
41
  end
48
42
  end
@@ -44,11 +44,15 @@ module RuboCop
44
44
  end
45
45
 
46
46
  def relevant_file?(file)
47
- rspec_pattern =~ file && super
47
+ relevant_rubocop_rspec_file?(file) && super
48
48
  end
49
49
 
50
50
  private
51
51
 
52
+ def relevant_rubocop_rspec_file?(file)
53
+ rspec_pattern =~ file
54
+ end
55
+
52
56
  def rspec_pattern
53
57
  Regexp.union(rspec_pattern_config.map(&Regexp.public_method(:new)))
54
58
  end
@@ -23,20 +23,20 @@ module RuboCop
23
23
  'the class or module being tested.'.freeze
24
24
 
25
25
  def_node_matcher :valid_describe?, <<-PATTERN
26
- {(send {(const nil :RSpec) nil} :describe const ...) (send nil :describe)}
26
+ {(send {(const nil :RSpec) nil} :describe const ...) (send nil :describe)}
27
27
  PATTERN
28
28
 
29
29
  def_node_matcher :describe_with_metadata, <<-PATTERN
30
- (send {(const nil :RSpec) nil} :describe
31
- !const
32
- ...
33
- (hash $...))
30
+ (send {(const nil :RSpec) nil} :describe
31
+ !const
32
+ ...
33
+ (hash $...))
34
34
  PATTERN
35
35
 
36
36
  def_node_matcher :rails_metadata?, <<-PATTERN
37
- (pair
38
- (sym :type)
39
- (sym {:request :feature :routing :view}))
37
+ (pair
38
+ (sym :type)
39
+ (sym {:request :feature :routing :view}))
40
40
  PATTERN
41
41
 
42
42
  def_node_matcher :shared_group?, <<-PATTERN
@@ -20,15 +20,16 @@ module RuboCop
20
20
  include RuboCop::RSpec::TopLevelDescribe,
21
21
  RuboCop::RSpec::Util
22
22
 
23
- MESSAGE = 'The second argument to describe should be the method ' \
24
- "being tested. '#instance' or '.class'".freeze
25
- METHOD_STRING_MATCHER = /^[\#\.].+/
23
+ MSG = 'The second argument to describe should be the method '\
24
+ "being tested. '#instance' or '.class'.".freeze
25
+
26
+ METHOD_STRING_MATCHER = /\A[\#\.].+/
26
27
 
27
28
  def on_top_level_describe(_node, (_, second_arg))
28
- return unless second_arg && second_arg.type.equal?(:str)
29
+ return unless second_arg && second_arg.str_type?
29
30
  return if METHOD_STRING_MATCHER =~ one(second_arg.children)
30
31
 
31
- add_offense(second_arg, :expression, MESSAGE)
32
+ add_offense(second_arg, :expression)
32
33
  end
33
34
  end
34
35
  end
@@ -34,10 +34,10 @@ module RuboCop
34
34
  #
35
35
  class DescribedClass < Cop
36
36
  include RuboCop::RSpec::TopLevelDescribe
37
- include RuboCop::Cop::ConfigurableEnforcedStyle
37
+ include ConfigurableEnforcedStyle
38
38
 
39
39
  DESCRIBED_CLASS = 'described_class'.freeze
40
- MSG = 'Use `%s` instead of `%s`'.freeze
40
+ MSG = 'Use `%s` instead of `%s`.'.freeze
41
41
 
42
42
  def_node_matcher :common_instance_exec_closure?, <<-PATTERN
43
43
  (block (send (const nil {:Class :Module}) :new ...) ...)
@@ -28,29 +28,26 @@ module RuboCop
28
28
  class ExampleLength < Cop
29
29
  include CodeLength
30
30
 
31
- EXAMPLE_BLOCKS = RuboCop::RSpec::Language::Examples::ALL
31
+ MSG = 'Example has too many lines [%d/%d].'.freeze
32
32
 
33
33
  def on_block(node)
34
- method, _args, _body = *node
35
- _receiver, method_name, _object = *method
36
- return unless EXAMPLE_BLOCKS.include?(method_name)
34
+ return unless example?(node)
37
35
 
38
36
  length = code_length(node)
39
37
 
40
38
  return unless length > max_length
39
+
41
40
  add_offense(node, :expression, message(length))
42
41
  end
43
42
 
44
43
  private
45
44
 
46
45
  def code_length(node)
47
- lines = node.source.lines[1..-2]
48
-
49
- lines.count { |line| !irrelevant_line(line) }
46
+ node.source.lines[1..-2].count { |line| !irrelevant_line(line) }
50
47
  end
51
48
 
52
49
  def message(length)
53
- format('Example has too many lines. [%d/%d]', length, max_length)
50
+ format(MSG, length, max_length)
54
51
  end
55
52
  end
56
53
  end
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
- # Checks that example descriptions do not start with "should".
6
+ # Checks for common mistakes in example descriptions.
7
+ #
8
+ # This cop will correct docstrings that begin with 'should' and 'it'.
7
9
  #
8
10
  # @see http://betterspecs.org/#should
9
11
  #
@@ -18,42 +20,74 @@ module RuboCop
18
20
  # # good
19
21
  # it 'finds nothing' do
20
22
  # end
23
+ #
24
+ # @example
25
+ # # bad
26
+ # it 'it does things' do
27
+ # end
28
+ #
29
+ # # good
30
+ # it 'does things' do
31
+ # end
21
32
  class ExampleWording < Cop
22
- MSG = 'Do not use should when describing your tests.'.freeze
33
+ MSG_SHOULD = 'Do not use should when describing your tests.'.freeze
34
+ MSG_IT = "Do not repeat 'it' when describing your tests.".freeze
23
35
 
24
- def on_block(node) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength,Metrics/LineLength
25
- method, = *node
26
- _, method_name, *args = *method
36
+ SHOULD_PREFIX = 'should'.freeze
37
+ IT_PREFIX = 'it '.freeze
27
38
 
28
- return unless method_name.equal?(:it)
39
+ def_node_matcher(
40
+ :it_description,
41
+ '(block (send _ :it $(str $_) ...) ...)'
42
+ )
29
43
 
30
- arguments = args.first.to_a
31
- message = arguments.first.to_s
32
- return unless message.downcase.start_with?('should')
44
+ def on_block(node)
45
+ it_description(node) do |description_node, message|
46
+ text = message.downcase
33
47
 
34
- arg1 = args.first.loc.expression
35
- message = Parser::Source::Range.new(arg1.source_buffer,
36
- arg1.begin_pos + 1,
37
- arg1.end_pos - 1)
38
-
39
- add_offense(message, message)
48
+ if text.start_with?(SHOULD_PREFIX)
49
+ add_wording_offense(description_node, MSG_SHOULD)
50
+ elsif text.start_with?(IT_PREFIX)
51
+ add_wording_offense(description_node, MSG_IT)
52
+ end
53
+ end
40
54
  end
41
55
 
42
56
  def autocorrect(range)
43
57
  lambda do |corrector|
44
- corrector.replace(
45
- range,
46
- RuboCop::RSpec::Wording.new(
47
- range.source,
48
- ignore: ignored_words,
49
- replace: custom_transform
50
- ).rewrite
51
- )
58
+ corrector.replace(range, replacement_text(range))
52
59
  end
53
60
  end
54
61
 
55
62
  private
56
63
 
64
+ def add_wording_offense(node, message)
65
+ expr = node.loc.expression
66
+
67
+ docstring =
68
+ Parser::Source::Range.new(
69
+ expr.source_buffer,
70
+ expr.begin_pos + 1,
71
+ expr.end_pos - 1
72
+ )
73
+
74
+ add_offense(docstring, docstring, message)
75
+ end
76
+
77
+ def replacement_text(range)
78
+ text = range.source
79
+
80
+ if text.start_with?('should')
81
+ RuboCop::RSpec::Wording.new(
82
+ text,
83
+ ignore: ignored_words,
84
+ replace: custom_transform
85
+ ).rewrite
86
+ elsif text.start_with?(IT_PREFIX)
87
+ text.sub(IT_PREFIX, '')
88
+ end
89
+ end
90
+
57
91
  def custom_transform
58
92
  cop_config.fetch('CustomTransform', {})
59
93
  end