rubocop-rspec 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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