rubocop-rspec 1.38.0 → 1.42.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +44 -0
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/README.md +1 -61
  5. data/config/default.yml +147 -17
  6. data/lib/rubocop-rspec.rb +3 -1
  7. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +11 -18
  8. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +11 -18
  9. data/lib/rubocop/cop/rspec/be.rb +1 -1
  10. data/lib/rubocop/cop/rspec/be_eql.rb +5 -5
  11. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +18 -16
  12. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +8 -9
  13. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +69 -0
  14. data/lib/rubocop/cop/rspec/context_method.rb +5 -7
  15. data/lib/rubocop/cop/rspec/cop.rb +9 -29
  16. data/lib/rubocop/cop/rspec/describe_class.rb +20 -5
  17. data/lib/rubocop/cop/rspec/describe_method.rb +0 -1
  18. data/lib/rubocop/cop/rspec/described_class.rb +10 -7
  19. data/lib/rubocop/cop/rspec/dialect.rb +4 -11
  20. data/lib/rubocop/cop/rspec/empty_hook.rb +46 -0
  21. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +5 -3
  22. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +5 -5
  23. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +4 -1
  24. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +5 -5
  25. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +4 -1
  26. data/lib/rubocop/cop/rspec/example_wording.rb +6 -7
  27. data/lib/rubocop/cop/rspec/expect_actual.rb +7 -10
  28. data/lib/rubocop/cop/rspec/expect_change.rb +9 -34
  29. data/lib/rubocop/cop/rspec/expect_in_hook.rb +2 -2
  30. data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
  31. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +23 -20
  32. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +10 -16
  33. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +6 -7
  34. data/lib/rubocop/cop/rspec/file_path.rb +32 -4
  35. data/lib/rubocop/cop/rspec/hook_argument.rb +11 -17
  36. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +9 -28
  37. data/lib/rubocop/cop/rspec/implicit_expect.rb +6 -14
  38. data/lib/rubocop/cop/rspec/implicit_subject.rb +8 -5
  39. data/lib/rubocop/cop/rspec/instance_spy.rb +17 -11
  40. data/lib/rubocop/cop/rspec/instance_variable.rb +3 -7
  41. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +2 -5
  42. data/lib/rubocop/cop/rspec/it_behaves_like.rb +4 -5
  43. data/lib/rubocop/cop/rspec/leading_subject.rb +12 -19
  44. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -4
  45. data/lib/rubocop/cop/rspec/let_before_examples.rb +9 -25
  46. data/lib/rubocop/cop/rspec/let_setup.rb +15 -3
  47. data/lib/rubocop/cop/rspec/message_chain.rb +6 -5
  48. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
  49. data/lib/rubocop/cop/rspec/message_spies.rb +1 -2
  50. data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -18
  51. data/lib/rubocop/cop/rspec/named_subject.rb +7 -7
  52. data/lib/rubocop/cop/rspec/nested_groups.rb +9 -10
  53. data/lib/rubocop/cop/rspec/not_to_not.rb +4 -5
  54. data/lib/rubocop/cop/rspec/predicate_matcher.rb +25 -55
  55. data/lib/rubocop/cop/rspec/rails/http_status.rb +6 -8
  56. data/lib/rubocop/cop/rspec/receive_counts.rb +14 -16
  57. data/lib/rubocop/cop/rspec/receive_never.rb +10 -10
  58. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +11 -1
  59. data/lib/rubocop/cop/rspec/return_from_stub.rb +11 -21
  60. data/lib/rubocop/cop/rspec/scattered_let.rb +11 -1
  61. data/lib/rubocop/cop/rspec/shared_context.rb +7 -20
  62. data/lib/rubocop/cop/rspec/shared_examples.rb +6 -8
  63. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +14 -17
  64. data/lib/rubocop/cop/rspec/subject_stub.rb +23 -51
  65. data/lib/rubocop/cop/rspec/variable_definition.rb +56 -0
  66. data/lib/rubocop/cop/rspec/variable_name.rb +47 -0
  67. data/lib/rubocop/cop/rspec/yield.rb +13 -10
  68. data/lib/rubocop/cop/rspec_cops.rb +5 -1
  69. data/lib/rubocop/rspec/blank_line_separation.rb +0 -8
  70. data/lib/rubocop/rspec/corrector/move_node.rb +52 -0
  71. data/lib/rubocop/rspec/description_extractor.rb +2 -6
  72. data/lib/rubocop/rspec/example.rb +1 -1
  73. data/lib/rubocop/rspec/example_group.rb +21 -49
  74. data/lib/rubocop/rspec/factory_bot.rb +7 -1
  75. data/lib/rubocop/rspec/language.rb +8 -0
  76. data/lib/rubocop/rspec/language/node_pattern.rb +5 -1
  77. data/lib/rubocop/rspec/top_level_group.rb +44 -0
  78. data/lib/rubocop/rspec/variable.rb +16 -0
  79. data/lib/rubocop/rspec/version.rb +1 -1
  80. metadata +17 -10
  81. data/lib/rubocop/rspec/util.rb +0 -19
@@ -11,18 +11,20 @@ require_relative 'rubocop/rspec/inject'
11
11
  require_relative 'rubocop/rspec/node'
12
12
  require_relative 'rubocop/rspec/top_level_describe'
13
13
  require_relative 'rubocop/rspec/wording'
14
- require_relative 'rubocop/rspec/util'
15
14
  require_relative 'rubocop/rspec/language'
16
15
  require_relative 'rubocop/rspec/language/node_pattern'
16
+ require_relative 'rubocop/rspec/top_level_group'
17
17
  require_relative 'rubocop/rspec/concept'
18
18
  require_relative 'rubocop/rspec/example_group'
19
19
  require_relative 'rubocop/rspec/example'
20
20
  require_relative 'rubocop/rspec/hook'
21
+ require_relative 'rubocop/rspec/variable'
21
22
  require_relative 'rubocop/cop/rspec/cop'
22
23
  require_relative 'rubocop/rspec/align_let_brace'
23
24
  require_relative 'rubocop/rspec/factory_bot'
24
25
  require_relative 'rubocop/rspec/final_end_location'
25
26
  require_relative 'rubocop/rspec/blank_line_separation'
27
+ require_relative 'rubocop/rspec/corrector/move_node'
26
28
 
27
29
  RuboCop::RSpec::Inject.defaults!
28
30
 
@@ -18,35 +18,28 @@ module RuboCop
18
18
  # let(:a) { b }
19
19
  #
20
20
  class AlignLeftLetBrace < Cop
21
+ extend AutoCorrector
22
+
21
23
  MSG = 'Align left let brace'
22
24
 
23
25
  def self.autocorrect_incompatible_with
24
26
  [Layout::ExtraSpacing]
25
27
  end
26
28
 
27
- def investigate(_processed_source)
29
+ def on_new_investigation
28
30
  return if processed_source.blank?
29
31
 
30
- token_aligner.offending_tokens.each do |let|
31
- add_offense(let, location: :begin)
32
- end
33
- end
32
+ token_aligner =
33
+ RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin)
34
34
 
35
- def autocorrect(let)
36
- lambda do |corrector|
37
- corrector.insert_before(
38
- let.loc.begin,
39
- token_aligner.indent_for(let)
40
- )
35
+ token_aligner.offending_tokens.each do |let|
36
+ add_offense(let.loc.begin) do |corrector|
37
+ corrector.insert_before(
38
+ let.loc.begin, token_aligner.indent_for(let)
39
+ )
40
+ end
41
41
  end
42
42
  end
43
-
44
- private
45
-
46
- def token_aligner
47
- @token_aligner ||=
48
- RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :begin)
49
- end
50
43
  end
51
44
  end
52
45
  end
@@ -18,35 +18,28 @@ module RuboCop
18
18
  # let(:a) { b }
19
19
  #
20
20
  class AlignRightLetBrace < Cop
21
+ extend AutoCorrector
22
+
21
23
  MSG = 'Align right let brace'
22
24
 
23
25
  def self.autocorrect_incompatible_with
24
26
  [Layout::ExtraSpacing]
25
27
  end
26
28
 
27
- def investigate(_processed_source)
29
+ def on_new_investigation
28
30
  return if processed_source.blank?
29
31
 
30
- token_aligner.offending_tokens.each do |let|
31
- add_offense(let, location: :end)
32
- end
33
- end
32
+ token_aligner =
33
+ RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end)
34
34
 
35
- def autocorrect(let)
36
- lambda do |corrector|
37
- corrector.insert_before(
38
- let.loc.end,
39
- token_aligner.indent_for(let)
40
- )
35
+ token_aligner.offending_tokens.each do |let|
36
+ add_offense(let.loc.end) do |corrector|
37
+ corrector.insert_before(
38
+ let.loc.end, token_aligner.indent_for(let)
39
+ )
40
+ end
41
41
  end
42
42
  end
43
-
44
- private
45
-
46
- def token_aligner
47
- @token_aligner ||=
48
- RuboCop::RSpec::AlignLetBrace.new(processed_source.ast, :end)
49
- end
50
43
  end
51
44
  end
52
45
  end
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  def on_send(node)
30
30
  be_without_args(node) do |matcher|
31
- add_offense(matcher, location: :selector)
31
+ add_offense(matcher.loc.selector)
32
32
  end
33
33
  end
34
34
  end
@@ -36,6 +36,8 @@ module RuboCop
36
36
  # coerce objects for comparison.
37
37
  #
38
38
  class BeEql < Cop
39
+ extend AutoCorrector
40
+
39
41
  MSG = 'Prefer `be` over `eql`.'
40
42
 
41
43
  def_node_matcher :eql_type_with_identity, <<-PATTERN
@@ -44,13 +46,11 @@ module RuboCop
44
46
 
45
47
  def on_send(node)
46
48
  eql_type_with_identity(node) do |eql|
47
- add_offense(eql, location: :selector)
49
+ add_offense(eql.loc.selector) do |corrector|
50
+ corrector.replace(eql.loc.selector, 'be')
51
+ end
48
52
  end
49
53
  end
50
-
51
- def autocorrect(node)
52
- ->(corrector) { corrector.replace(node.loc.selector, 'be') }
53
- end
54
54
  end
55
55
  end
56
56
  end
@@ -24,6 +24,8 @@ module RuboCop
24
24
  # expect(page).to have_current_path(/widgets/)
25
25
  #
26
26
  class CurrentPathExpectation < Cop
27
+ extend AutoCorrector
28
+
27
29
  MSG = 'Do not set an RSpec expectation on `current_path` in ' \
28
30
  'Capybara feature specs - instead, use the ' \
29
31
  '`have_current_path` matcher on `page`'
@@ -47,30 +49,30 @@ module RuboCop
47
49
 
48
50
  def on_send(node)
49
51
  expectation_set_on_current_path(node) do
50
- add_offense(node, location: :selector)
52
+ add_offense(node.loc.selector) do |corrector|
53
+ next unless node.chained?
54
+
55
+ autocorrect(corrector, node)
56
+ end
51
57
  end
52
58
  end
53
59
 
54
- def autocorrect(node)
55
- lambda do |corrector|
56
- return unless node.chained?
60
+ private
57
61
 
58
- as_is_matcher(node.parent) do |to_sym, matcher_node|
59
- rewrite_expectation(corrector, node, to_sym, matcher_node)
60
- end
62
+ def autocorrect(corrector, node)
63
+ as_is_matcher(node.parent) do |to_sym, matcher_node|
64
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
65
+ end
61
66
 
62
- regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp|
63
- rewrite_expectation(corrector, node, to_sym, matcher_node)
64
- convert_regexp_str_to_literal(corrector, matcher_node, regexp)
65
- end
67
+ regexp_str_matcher(node.parent) do |to_sym, matcher_node, regexp|
68
+ rewrite_expectation(corrector, node, to_sym, matcher_node)
69
+ convert_regexp_str_to_literal(corrector, matcher_node, regexp)
66
70
  end
67
71
  end
68
72
 
69
- private
70
-
71
73
  def rewrite_expectation(corrector, node, to_symbol, matcher_node)
72
74
  current_path_node = node.first_argument
73
- corrector.replace(current_path_node.loc.expression, 'page')
75
+ corrector.replace(current_path_node, 'page')
74
76
  corrector.replace(node.parent.loc.selector, 'to')
75
77
  matcher_method = if to_symbol == :to
76
78
  'have_current_path'
@@ -84,7 +86,7 @@ module RuboCop
84
86
  def convert_regexp_str_to_literal(corrector, matcher_node, regexp_str)
85
87
  str_node = matcher_node.first_argument
86
88
  regexp_expr = Regexp.new(regexp_str).inspect
87
- corrector.replace(str_node.loc.expression, regexp_expr)
89
+ corrector.replace(str_node, regexp_expr)
88
90
  end
89
91
 
90
92
  # `have_current_path` with no options will include the querystring
@@ -97,7 +99,7 @@ module RuboCop
97
99
  return if %i[regexp str].include?(expectation_last_child.type)
98
100
 
99
101
  corrector.insert_after(
100
- expectation_last_child.loc.expression,
102
+ expectation_last_child,
101
103
  ', ignore_query: true'
102
104
  )
103
105
  end
@@ -41,6 +41,8 @@ module RuboCop
41
41
  # end
42
42
  # end
43
43
  class FeatureMethods < Cop
44
+ extend AutoCorrector
45
+
44
46
  MSG = 'Use `%<replacement>s` instead of `%<method>s`.'
45
47
 
46
48
  # https://git.io/v7Kwr
@@ -71,18 +73,15 @@ module RuboCop
71
73
  feature_method(node) do |send_node, match|
72
74
  next if enabled?(match)
73
75
 
74
- add_offense(
75
- send_node,
76
- location: :selector,
77
- message: format(MSG, method: match, replacement: MAP[match])
78
- )
76
+ add_offense(send_node.loc.selector) do |corrector|
77
+ corrector.replace(send_node.loc.selector, MAP[match].to_s)
78
+ end
79
79
  end
80
80
  end
81
81
 
82
- def autocorrect(node)
83
- lambda do |corrector|
84
- corrector.replace(node.loc.selector, MAP[node.method_name].to_s)
85
- end
82
+ def message(range)
83
+ name = range.source.to_sym
84
+ format(MSG, method: name, replacement: MAP[name])
86
85
  end
87
86
 
88
87
  private
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module Capybara
7
+ # Checks for boolean visibility in capybara finders.
8
+ #
9
+ # Capybara lets you find elements that match a certain visibility using
10
+ # the `:visible` option. `:visible` accepts both boolean and symbols as
11
+ # values, however using booleans can have unwanted effects. `visible:
12
+ # false` does not find just invisible elements, but both visible and
13
+ # invisible elements. For expressiveness and clarity, use one of the
14
+ # symbol values, `:all`, `:hidden` or `:visible`.
15
+ # (https://www.rubydoc.info/gems/capybara/Capybara%2FNode%2FFinders:all)
16
+ #
17
+ # @example
18
+ #
19
+ # # bad
20
+ # expect(page).to have_selector('.foo', visible: false)
21
+ # expect(page).to have_css('.foo', visible: true)
22
+ # expect(page).to have_link('my link', visible: false)
23
+ #
24
+ # # good
25
+ # expect(page).to have_selector('.foo', visible: :visible)
26
+ # expect(page).to have_css('.foo', visible: :all)
27
+ # expect(page).to have_link('my link', visible: :hidden)
28
+ #
29
+ class VisibilityMatcher < Cop
30
+ MSG_FALSE = 'Use `:all` or `:hidden` instead of `false`.'
31
+ MSG_TRUE = 'Use `:visible` instead of `true`.'
32
+ CAPYBARA_MATCHER_METHODS = %i[
33
+ have_selector
34
+ have_css
35
+ have_xpath
36
+ have_link
37
+ have_button
38
+ have_field
39
+ have_select
40
+ have_table
41
+ have_checked_field
42
+ have_unchecked_field
43
+ have_text
44
+ have_content
45
+ ].freeze
46
+
47
+ def_node_matcher :visible_true?, <<~PATTERN
48
+ (send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) true) ...>))
49
+ PATTERN
50
+
51
+ def_node_matcher :visible_false?, <<~PATTERN
52
+ (send nil? #capybara_matcher? ... (hash <$(pair (sym :visible) false) ...>))
53
+ PATTERN
54
+
55
+ def on_send(node)
56
+ visible_false?(node) { |arg| add_offense(arg, message: MSG_FALSE) }
57
+ visible_true?(node) { |arg| add_offense(arg, message: MSG_TRUE) }
58
+ end
59
+
60
+ private
61
+
62
+ def capybara_matcher?(method_name)
63
+ CAPYBARA_MATCHER_METHODS.include? method_name
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -24,6 +24,8 @@ module RuboCop
24
24
  # # ...
25
25
  # end
26
26
  class ContextMethod < Cop
27
+ extend AutoCorrector
28
+
27
29
  MSG = 'Use `describe` for testing methods.'
28
30
 
29
31
  def_node_matcher :context_method, <<-PATTERN
@@ -32,13 +34,9 @@ module RuboCop
32
34
 
33
35
  def on_block(node)
34
36
  context_method(node) do |context|
35
- add_offense(context)
36
- end
37
- end
38
-
39
- def autocorrect(node)
40
- lambda do |corrector|
41
- corrector.replace(node.parent.loc.selector, 'describe')
37
+ add_offense(context) do |corrector|
38
+ corrector.replace(node.send_node.loc.selector, 'describe')
39
+ end
42
40
  end
43
41
  end
44
42
 
@@ -1,29 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RuboCop
4
- module Cop # rubocop:disable Style/Documentation
5
- WorkaroundCop = Cop.dup
6
-
7
- # Clone of the the normal RuboCop::Cop::Cop class so we can rewrite
8
- # the inherited method without breaking functionality
9
- class WorkaroundCop
10
- # Remove the Cop.inherited method to be a noop. Our RSpec::Cop
11
- # class will invoke the inherited hook instead
12
- class << self
13
- undef inherited
14
- def inherited(*) end
15
- end
16
-
17
- # Special case `Module#<` so that the rspec support rubocop exports
18
- # is compatible with our subclass
19
- def self.<(other)
20
- other.equal?(RuboCop::Cop::Cop) || super
21
- end
22
- end
23
- private_constant(:WorkaroundCop)
24
-
4
+ module Cop
25
5
  module RSpec
26
- # @abstract parent class to rspec cops
6
+ # @abstract parent class to RSpec cops
27
7
  #
28
8
  # The criteria for whether rubocop-rspec analyzes a certain ruby file
29
9
  # is configured via `AllCops/RSpec`. For example, if you want to
@@ -31,13 +11,13 @@ module RuboCop
31
11
  # then you could add this to your configuration:
32
12
  #
33
13
  # @example configuring analyzed paths
34
- #
35
- # AllCops:
36
- # RSpec:
37
- # Patterns:
38
- # - '_test.rb$'
39
- # - '(?:^|/)test/'
40
- class Cop < WorkaroundCop
14
+ # # .rubocop.yml
15
+ # # AllCops:
16
+ # # RSpec:
17
+ # # Patterns:
18
+ # # - '_test.rb$'
19
+ # # - '(?:^|/)test/'
20
+ class Cop < ::RuboCop::Cop::Base
41
21
  include RuboCop::RSpec::Language
42
22
  include RuboCop::RSpec::Language::NodePattern
43
23
 
@@ -12,6 +12,11 @@ module RuboCop
12
12
  #
13
13
  # # good
14
14
  # describe TestedClass do
15
+ # subject { described_class }
16
+ # end
17
+ #
18
+ # describe 'TestedClass::VERSION' do
19
+ # subject { Object.const_get(self.class.description) }
15
20
  # end
16
21
  #
17
22
  # describe "A feature example", type: :feature do
@@ -38,18 +43,28 @@ module RuboCop
38
43
  def_node_matcher :rails_metadata?, <<-PATTERN
39
44
  (pair
40
45
  (sym :type)
41
- (sym {:request :feature :system :routing :view})
46
+ (sym {
47
+ :channel :controller :helper :job :mailer :model :request
48
+ :routing :view :feature :system :mailbox
49
+ }
50
+ )
42
51
  )
43
52
  PATTERN
44
53
 
45
- def_node_matcher :shared_group?, SharedGroups::ALL.block_pattern
46
-
47
- def on_top_level_describe(node, args)
54
+ def on_top_level_describe(node, (described_value, _))
48
55
  return if shared_group?(root_node)
49
56
  return if valid_describe?(node)
50
57
  return if describe_with_rails_metadata?(node)
58
+ return if string_constant_describe?(described_value)
59
+
60
+ add_offense(described_value)
61
+ end
62
+
63
+ private
51
64
 
52
- add_offense(args.first)
65
+ def string_constant_describe?(described_value)
66
+ described_value.str_type? &&
67
+ described_value.value =~ /^((::)?[A-Z]\w*)+$/
53
68
  end
54
69
  end
55
70
  end