rubocop-rspec 2.11.1 → 2.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +125 -81
  3. data/config/default.yml +59 -6
  4. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +8 -9
  5. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +8 -9
  6. data/lib/rubocop/cop/rspec/any_instance.rb +1 -0
  7. data/lib/rubocop/cop/rspec/around_block.rb +26 -3
  8. data/lib/rubocop/cop/rspec/be.rb +0 -1
  9. data/lib/rubocop/cop/rspec/be_eq.rb +0 -1
  10. data/lib/rubocop/cop/rspec/be_eql.rb +0 -1
  11. data/lib/rubocop/cop/rspec/before_after_all.rb +1 -0
  12. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +9 -3
  13. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +2 -1
  14. data/lib/rubocop/cop/rspec/capybara/specific_finders.rb +86 -0
  15. data/lib/rubocop/cop/rspec/capybara/specific_matcher.rb +158 -0
  16. data/lib/rubocop/cop/rspec/capybara/visibility_matcher.rb +0 -1
  17. data/lib/rubocop/cop/rspec/change_by_zero.rb +67 -7
  18. data/lib/rubocop/cop/rspec/class_check.rb +101 -0
  19. data/lib/rubocop/cop/rspec/context_method.rb +2 -1
  20. data/lib/rubocop/cop/rspec/context_wording.rb +49 -18
  21. data/lib/rubocop/cop/rspec/describe_class.rb +1 -1
  22. data/lib/rubocop/cop/rspec/describe_method.rb +1 -0
  23. data/lib/rubocop/cop/rspec/described_class.rb +5 -15
  24. data/lib/rubocop/cop/rspec/dialect.rb +1 -0
  25. data/lib/rubocop/cop/rspec/empty_example_group.rb +19 -4
  26. data/lib/rubocop/cop/rspec/empty_hook.rb +4 -5
  27. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +4 -9
  28. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  29. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +2 -1
  30. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +32 -2
  31. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -1
  32. data/lib/rubocop/cop/rspec/example_length.rb +3 -2
  33. data/lib/rubocop/cop/rspec/example_without_description.rb +3 -2
  34. data/lib/rubocop/cop/rspec/example_wording.rb +2 -1
  35. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +1 -0
  36. data/lib/rubocop/cop/rspec/expect_actual.rb +5 -0
  37. data/lib/rubocop/cop/rspec/expect_change.rb +9 -9
  38. data/lib/rubocop/cop/rspec/expect_in_hook.rb +4 -1
  39. data/lib/rubocop/cop/rspec/expect_output.rb +1 -0
  40. data/lib/rubocop/cop/rspec/factory_bot/attribute_defined_statically.rb +2 -1
  41. data/lib/rubocop/cop/rspec/factory_bot/create_list.rb +50 -13
  42. data/lib/rubocop/cop/rspec/factory_bot/factory_class_name.rb +1 -0
  43. data/lib/rubocop/cop/rspec/factory_bot/syntax_methods.rb +1 -1
  44. data/lib/rubocop/cop/rspec/file_path.rb +8 -4
  45. data/lib/rubocop/cop/rspec/focus.rb +20 -4
  46. data/lib/rubocop/cop/rspec/hook_argument.rb +10 -5
  47. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +10 -9
  48. data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +0 -1
  49. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +1 -0
  50. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -3
  51. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  52. data/lib/rubocop/cop/rspec/instance_variable.rb +0 -1
  53. data/lib/rubocop/cop/rspec/it_behaves_like.rb +3 -2
  54. data/lib/rubocop/cop/rspec/iterated_expectation.rb +16 -0
  55. data/lib/rubocop/cop/rspec/leading_subject.rb +15 -15
  56. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  57. data/lib/rubocop/cop/rspec/let_before_examples.rb +7 -8
  58. data/lib/rubocop/cop/rspec/let_setup.rb +4 -4
  59. data/lib/rubocop/cop/rspec/message_chain.rb +1 -1
  60. data/lib/rubocop/cop/rspec/message_expectation.rb +1 -1
  61. data/lib/rubocop/cop/rspec/message_spies.rb +7 -1
  62. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +2 -1
  63. data/lib/rubocop/cop/rspec/mixin/css_selector.rb +99 -0
  64. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +13 -4
  65. data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
  66. data/lib/rubocop/cop/rspec/multiple_describes.rb +1 -0
  67. data/lib/rubocop/cop/rspec/multiple_expectations.rb +19 -3
  68. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +1 -3
  69. data/lib/rubocop/cop/rspec/multiple_subjects.rb +17 -2
  70. data/lib/rubocop/cop/rspec/named_subject.rb +2 -1
  71. data/lib/rubocop/cop/rspec/nested_groups.rb +45 -25
  72. data/lib/rubocop/cop/rspec/no_expectation_example.rb +64 -0
  73. data/lib/rubocop/cop/rspec/not_to_not.rb +13 -1
  74. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  75. data/lib/rubocop/cop/rspec/pending.rb +1 -0
  76. data/lib/rubocop/cop/rspec/predicate_matcher.rb +2 -1
  77. data/lib/rubocop/cop/rspec/rails/avoid_setup_hook.rb +1 -2
  78. data/lib/rubocop/cop/rspec/rails/have_http_status.rb +47 -0
  79. data/lib/rubocop/cop/rspec/receive_counts.rb +14 -15
  80. data/lib/rubocop/cop/rspec/receive_never.rb +4 -5
  81. data/lib/rubocop/cop/rspec/repeated_description.rb +25 -26
  82. data/lib/rubocop/cop/rspec/repeated_example.rb +1 -1
  83. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +28 -29
  84. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +28 -29
  85. data/lib/rubocop/cop/rspec/repeated_include_example.rb +32 -33
  86. data/lib/rubocop/cop/rspec/return_from_stub.rb +12 -12
  87. data/lib/rubocop/cop/rspec/scattered_let.rb +1 -5
  88. data/lib/rubocop/cop/rspec/scattered_setup.rb +2 -2
  89. data/lib/rubocop/cop/rspec/shared_context.rb +1 -1
  90. data/lib/rubocop/cop/rspec/stubbed_mock.rb +0 -1
  91. data/lib/rubocop/cop/rspec/subject_declaration.rb +0 -1
  92. data/lib/rubocop/cop/rspec/subject_stub.rb +2 -2
  93. data/lib/rubocop/cop/rspec/unspecified_exception.rb +15 -15
  94. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -0
  95. data/lib/rubocop/cop/rspec/variable_name.rb +6 -7
  96. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -0
  97. data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
  98. data/lib/rubocop/cop/rspec/yield.rb +3 -2
  99. data/lib/rubocop/cop/rspec_cops.rb +5 -0
  100. data/lib/rubocop/rspec/config_formatter.rb +14 -3
  101. data/lib/rubocop/rspec/inject.rb +1 -3
  102. data/lib/rubocop/rspec/language/node_pattern.rb +4 -0
  103. data/lib/rubocop/rspec/language.rb +6 -1
  104. data/lib/rubocop/rspec/node.rb +1 -1
  105. data/lib/rubocop/rspec/version.rb +1 -1
  106. data/lib/rubocop/rspec/wording.rb +2 -2
  107. data/lib/rubocop/rspec.rb +14 -0
  108. data/lib/rubocop-rspec.rb +3 -0
  109. metadata +12 -88
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # styles: "implicit", "each", and "example." All styles have
11
11
  # the same behavior.
12
12
  #
13
- # @example when configuration is `EnforcedStyle: implicit`
13
+ # @example `EnforcedStyle: implicit` (default)
14
14
  # # bad
15
15
  # before(:each) do
16
16
  # # ...
@@ -26,7 +26,7 @@ module RuboCop
26
26
  # # ...
27
27
  # end
28
28
  #
29
- # @example when configuration is `EnforcedStyle: each`
29
+ # @example `EnforcedStyle: each`
30
30
  # # bad
31
31
  # before(:example) do
32
32
  # # ...
@@ -42,7 +42,7 @@ module RuboCop
42
42
  # # ...
43
43
  # end
44
44
  #
45
- # @example when configuration is `EnforcedStyle: example`
45
+ # @example `EnforcedStyle: example`
46
46
  # # bad
47
47
  # before(:each) do
48
48
  # # ...
@@ -57,6 +57,7 @@ module RuboCop
57
57
  # before(:example) do
58
58
  # # ...
59
59
  # end
60
+ #
60
61
  class HookArgument < Base
61
62
  extend AutoCorrector
62
63
  include ConfigurableEnforcedStyle
@@ -66,11 +67,13 @@ module RuboCop
66
67
 
67
68
  # @!method scoped_hook(node)
68
69
  def_node_matcher :scoped_hook, <<-PATTERN
69
- (block $(send _ #Hooks.all (sym ${:each :example})) ...)
70
+ ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
70
71
  PATTERN
71
72
 
72
73
  # @!method unscoped_hook(node)
73
- def_node_matcher :unscoped_hook, '(block $(send _ #Hooks.all) ...)'
74
+ def_node_matcher :unscoped_hook, <<-PATTERN
75
+ ({block numblock} $(send _ #Hooks.all) ...)
76
+ PATTERN
74
77
 
75
78
  def on_block(node)
76
79
  hook(node) do |method_send, scope_name|
@@ -86,6 +89,8 @@ module RuboCop
86
89
  end
87
90
  end
88
91
 
92
+ alias on_numblock on_block
93
+
89
94
  private
90
95
 
91
96
  def check_implicit(method_send)
@@ -6,8 +6,7 @@ module RuboCop
6
6
  # Checks for before/around/after hooks that come after an example.
7
7
  #
8
8
  # @example
9
- # # Bad
10
- #
9
+ # # bad
11
10
  # it 'checks what foo does' do
12
11
  # expect(foo).to be
13
12
  # end
@@ -15,7 +14,7 @@ module RuboCop
15
14
  # before { prepare }
16
15
  # after { clean_up }
17
16
  #
18
- # # Good
17
+ # # good
19
18
  # before { prepare }
20
19
  # after { clean_up }
21
20
  #
@@ -32,6 +31,7 @@ module RuboCop
32
31
  def_node_matcher :example_or_group?, <<-PATTERN
33
32
  {
34
33
  #{block_pattern('{#ExampleGroups.all #Examples.all}')}
34
+ #{numblock_pattern('{#ExampleGroups.all #Examples.all}')}
35
35
  #{send_pattern('#Includes.examples')}
36
36
  }
37
37
  PATTERN
@@ -42,6 +42,8 @@ module RuboCop
42
42
  check_hooks(node.body) if multiline_block?(node.body)
43
43
  end
44
44
 
45
+ alias on_numblock on_block
46
+
45
47
  private
46
48
 
47
49
  def multiline_block?(block)
@@ -52,13 +54,12 @@ module RuboCop
52
54
  first_example = find_first_example(node)
53
55
  return unless first_example
54
56
 
55
- node.each_child_node do |child|
56
- next if child.sibling_index < first_example.sibling_index
57
- next unless hook?(child)
57
+ first_example.right_siblings.each do |sibling|
58
+ next unless hook?(sibling)
58
59
 
59
- msg = format(MSG, hook: child.method_name)
60
- add_offense(child, message: msg) do |corrector|
61
- autocorrect(corrector, child, first_example)
60
+ msg = format(MSG, hook: sibling.method_name)
61
+ add_offense(sibling, message: msg) do |corrector|
62
+ autocorrect(corrector, sibling, first_example)
62
63
  end
63
64
  end
64
65
  end
@@ -6,7 +6,6 @@ module RuboCop
6
6
  # Checks for equality assertions with identical expressions on both sides.
7
7
  #
8
8
  # @example
9
- #
10
9
  # # bad
11
10
  # expect(foo.bar).to eq(foo.bar)
12
11
  # expect(foo.bar).to eql(foo.bar)
@@ -16,6 +16,7 @@ module RuboCop
16
16
  # it 'changes something to a new value' do
17
17
  # expect { do_something }.to change(something).to(new_value)
18
18
  # end
19
+ #
19
20
  class ImplicitBlockExpectation < Base
20
21
  MSG = 'Avoid implicit block expectations.'
21
22
  RESTRICT_ON_SEND = %i[is_expected should should_not].freeze
@@ -8,8 +8,7 @@ module RuboCop
8
8
  # This cop can be configured using the `EnforcedStyle` option
9
9
  # and supports the `--auto-gen-config` flag.
10
10
  #
11
- # @example `EnforcedStyle: is_expected`
12
- #
11
+ # @example `EnforcedStyle: is_expected` (default)
13
12
  # # bad
14
13
  # it { should be_truthy }
15
14
  #
@@ -17,7 +16,6 @@ module RuboCop
17
16
  # it { is_expected.to be_truthy }
18
17
  #
19
18
  # @example `EnforcedStyle: should`
20
- #
21
19
  # # bad
22
20
  # it { is_expected.to be_truthy }
23
21
  #
@@ -42,7 +42,7 @@ module RuboCop
42
42
  ...)
43
43
  PATTERN
44
44
 
45
- def on_block(node)
45
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
46
46
  return unless example?(node)
47
47
 
48
48
  null_double(node) do |var, receiver|
@@ -24,7 +24,6 @@ module RuboCop
24
24
  # end
25
25
  #
26
26
  # @example with AssignmentOnly configuration
27
- #
28
27
  # # rubocop.yml
29
28
  # # RSpec/InstanceVariable:
30
29
  # # AssignmentOnly: false
@@ -5,19 +5,20 @@ module RuboCop
5
5
  module RSpec
6
6
  # Checks that only one `it_behaves_like` style is used.
7
7
  #
8
- # @example when configuration is `EnforcedStyle: it_behaves_like`
8
+ # @example `EnforcedStyle: it_behaves_like` (default)
9
9
  # # bad
10
10
  # it_should_behave_like 'a foo'
11
11
  #
12
12
  # # good
13
13
  # it_behaves_like 'a foo'
14
14
  #
15
- # @example when configuration is `EnforcedStyle: it_should_behave_like`
15
+ # @example `EnforcedStyle: it_should_behave_like`
16
16
  # # bad
17
17
  # it_behaves_like 'a foo'
18
18
  #
19
19
  # # good
20
20
  # it_should_behave_like 'a foo'
21
+ #
21
22
  class ItBehavesLike < Base
22
23
  extend AutoCorrector
23
24
  include ConfigurableEnforcedStyle
@@ -15,6 +15,7 @@ module RuboCop
15
15
  # it 'validates users' do
16
16
  # expect([user1, user2, user3]).to all(be_valid)
17
17
  # end
18
+ #
18
19
  class IteratedExpectation < Base
19
20
  MSG = 'Prefer using the `all` matcher instead ' \
20
21
  'of iterating over an array.'
@@ -28,6 +29,13 @@ module RuboCop
28
29
  )
29
30
  PATTERN
30
31
 
32
+ # @!method each_numblock?(node)
33
+ def_node_matcher :each_numblock?, <<-PATTERN
34
+ (numblock
35
+ (send ... :each) _ $(...)
36
+ )
37
+ PATTERN
38
+
31
39
  # @!method expectation?(node)
32
40
  def_node_matcher :expectation?, <<-PATTERN
33
41
  (send (send nil? :expect (lvar %)) :to ...)
@@ -41,6 +49,14 @@ module RuboCop
41
49
  end
42
50
  end
43
51
 
52
+ def on_numblock(node)
53
+ each_numblock?(node) do |body|
54
+ if single_expectation?(body, :_1) || only_expectations?(body, :_1)
55
+ add_offense(node.send_node)
56
+ end
57
+ end
58
+ end
59
+
44
60
  private
45
61
 
46
62
  def single_expectation?(body, arg)
@@ -7,29 +7,29 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # let(:params) { blah }
11
- # subject { described_class.new(params) }
10
+ # let(:params) { blah }
11
+ # subject { described_class.new(params) }
12
12
  #
13
- # before { do_something }
14
- # subject { described_class.new(params) }
13
+ # before { do_something }
14
+ # subject { described_class.new(params) }
15
15
  #
16
- # it { expect_something }
17
- # subject { described_class.new(params) }
18
- # it { expect_something_else }
16
+ # it { expect_something }
17
+ # subject { described_class.new(params) }
18
+ # it { expect_something_else }
19
19
  #
20
20
  #
21
21
  # # good
22
- # subject { described_class.new(params) }
23
- # let(:params) { blah }
22
+ # subject { described_class.new(params) }
23
+ # let(:params) { blah }
24
24
  #
25
25
  # # good
26
- # subject { described_class.new(params) }
27
- # before { do_something }
26
+ # subject { described_class.new(params) }
27
+ # before { do_something }
28
28
  #
29
29
  # # good
30
- # subject { described_class.new(params) }
31
- # it { expect_something }
32
- # it { expect_something_else }
30
+ # subject { described_class.new(params) }
31
+ # it { expect_something }
32
+ # it { expect_something_else }
33
33
  #
34
34
  class LeadingSubject < Base
35
35
  extend AutoCorrector
@@ -37,7 +37,7 @@ module RuboCop
37
37
 
38
38
  MSG = 'Declare `subject` above any other `%<offending>s` declarations.'
39
39
 
40
- def on_block(node)
40
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
41
41
  return unless subject?(node)
42
42
  return unless inside_example_group?(node)
43
43
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  #
11
11
  # If several examples may define a `DummyClass`, instead of being a
12
12
  # blank slate class as it will be in the first example, subsequent
13
- # examples will be reopening it and modifying its behaviour in
13
+ # examples will be reopening it and modifying its behavior in
14
14
  # unpredictable ways.
15
15
  # Even worse when a class that exists in the codebase is reopened.
16
16
  #
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for `let` definitions that come after an example.
7
7
  #
8
8
  # @example
9
- # # Bad
9
+ # # bad
10
10
  # let(:foo) { bar }
11
11
  #
12
12
  # it 'checks what foo does' do
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # expect(some).to be
20
20
  # end
21
21
  #
22
- # # Good
22
+ # # good
23
23
  # let(:foo) { bar }
24
24
  # let(:some) { other }
25
25
  #
@@ -43,7 +43,7 @@ module RuboCop
43
43
  }
44
44
  PATTERN
45
45
 
46
- def on_block(node)
46
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
47
  return unless example_group_with_body?(node)
48
48
 
49
49
  check_let_declarations(node.body) if multiline_block?(node.body)
@@ -59,12 +59,11 @@ module RuboCop
59
59
  first_example = find_first_example(node)
60
60
  return unless first_example
61
61
 
62
- node.each_child_node do |child|
63
- next if child.sibling_index < first_example.sibling_index
64
- next unless let?(child)
62
+ first_example.right_siblings.each do |sibling|
63
+ next unless let?(sibling)
65
64
 
66
- add_offense(child) do |corrector|
67
- autocorrect(corrector, child, first_example)
65
+ add_offense(sibling) do |corrector|
66
+ autocorrect(corrector, sibling, first_example)
68
67
  end
69
68
  end
70
69
  end
@@ -6,20 +6,20 @@ module RuboCop
6
6
  # Checks unreferenced `let!` calls being used for test setup.
7
7
  #
8
8
  # @example
9
- # # Bad
9
+ # # bad
10
10
  # let!(:my_widget) { create(:widget) }
11
11
  #
12
12
  # it 'counts widgets' do
13
13
  # expect(Widget.count).to eq(1)
14
14
  # end
15
15
  #
16
- # # Good
16
+ # # good
17
17
  # it 'counts widgets' do
18
18
  # create(:widget)
19
19
  # expect(Widget.count).to eq(1)
20
20
  # end
21
21
  #
22
- # # Good
22
+ # # good
23
23
  # before { create(:widget) }
24
24
  #
25
25
  # it 'counts widgets' do
@@ -49,7 +49,7 @@ module RuboCop
49
49
  # @!method method_called?(node)
50
50
  def_node_search :method_called?, '(send nil? %)'
51
51
 
52
- def on_block(node)
52
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
53
53
  return unless example_or_shared_group_or_including?(node)
54
54
 
55
55
  unused_let_bang(node) do |let|
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # # bad
10
10
  # allow(foo).to receive_message_chain(:bar, :baz).and_return(42)
11
11
  #
12
- # # better
12
+ # # good
13
13
  # thing = Thing.new(baz: 42)
14
14
  # allow(foo).to receive(:bar).and_return(thing)
15
15
  #
@@ -8,7 +8,7 @@ module RuboCop
8
8
  # This cop can be configured in your configuration using the
9
9
  # `EnforcedStyle` option and supports `--auto-gen-config`.
10
10
  #
11
- # @example `EnforcedStyle: allow`
11
+ # @example `EnforcedStyle: allow` (default)
12
12
  #
13
13
  # # bad
14
14
  # expect(foo).to receive(:bar)
@@ -8,21 +8,27 @@ module RuboCop
8
8
  # This cop can be configured in your configuration using the
9
9
  # `EnforcedStyle` option and supports `--auto-gen-config`.
10
10
  #
11
- # @example `EnforcedStyle: have_received`
11
+ # @example `EnforcedStyle: have_received` (default)
12
12
  #
13
13
  # # bad
14
14
  # expect(foo).to receive(:bar)
15
+ # do_something
15
16
  #
16
17
  # # good
18
+ # allow(foo).to receive(:bar) # or use instance_spy
19
+ # do_something
17
20
  # expect(foo).to have_received(:bar)
18
21
  #
19
22
  # @example `EnforcedStyle: receive`
20
23
  #
21
24
  # # bad
25
+ # allow(foo).to receive(:bar)
26
+ # do_something
22
27
  # expect(foo).to have_received(:bar)
23
28
  #
24
29
  # # good
25
30
  # expect(foo).to receive(:bar)
31
+ # do_something
26
32
  #
27
33
  class MessageSpies < Base
28
34
  include ConfigurableEnforcedStyle
@@ -19,10 +19,11 @@ module RuboCop
19
19
  #
20
20
  # describe "A feature example" do
21
21
  # end
22
+ #
22
23
  class MissingExampleGroupArgument < Base
23
24
  MSG = 'The first argument to `%<method>s` should not be empty.'
24
25
 
25
- def on_block(node)
26
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
26
27
  return unless example_group?(node)
27
28
  return if node.send_node.arguments?
28
29
 
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps parsing css selector.
7
+ module CssSelector
8
+ COMMON_OPTIONS = %w[
9
+ above below left_of right_of near count minimum maximum between text
10
+ id class style visible obscured exact exact_text normalize_ws match
11
+ wait filter_set focused
12
+ ].freeze
13
+
14
+ module_function
15
+
16
+ # @param selector [String]
17
+ # @return [Boolean]
18
+ # @example
19
+ # id?('#some-id') # => true
20
+ # id?('.some-class') # => false
21
+ def id?(selector)
22
+ selector.start_with?('#')
23
+ end
24
+
25
+ # @param selector [String]
26
+ # @return [Boolean]
27
+ # @example
28
+ # attribute?('[attribute]') # => true
29
+ # attribute?('attribute') # => false
30
+ def attribute?(selector)
31
+ selector.start_with?('[')
32
+ end
33
+
34
+ # @param selector [String]
35
+ # @return [Array<String>]
36
+ # @example
37
+ # attributes('a[foo-bar_baz]') # => {"foo-bar_baz=>true}
38
+ # attributes('button[foo][bar]') # => {"foo"=>true, "bar"=>true}
39
+ # attributes('table[foo=bar]') # => {"foo"=>"'bar'"}
40
+ def attributes(selector)
41
+ selector.scan(/\[(.*?)\]/).flatten.to_h do |attr|
42
+ key, value = attr.split('=')
43
+ [key, normalize_value(value)]
44
+ end
45
+ end
46
+
47
+ # @param selector [String]
48
+ # @return [Boolean]
49
+ # @example
50
+ # common_attributes?('a[focused]') # => true
51
+ # common_attributes?('button[focused][visible]') # => true
52
+ # common_attributes?('table[id=some-id]') # => true
53
+ # common_attributes?('h1[invalid]') # => false
54
+ def common_attributes?(selector)
55
+ attributes(selector).keys.difference(COMMON_OPTIONS).none?
56
+ end
57
+
58
+ # @param selector [String]
59
+ # @return [Array<String>]
60
+ # @example
61
+ # pseudo_classes('button:not([disabled])') # => ['not()']
62
+ # pseudo_classes('a:enabled:not([valid])') # => ['enabled', 'not()']
63
+ def pseudo_classes(selector)
64
+ # Attributes must be excluded or else the colon in the `href`s URL
65
+ # will also be picked up as pseudo classes.
66
+ # "a:not([href='http://example.com']):enabled" => "a:not():enabled"
67
+ ignored_attribute = selector.gsub(/\[.*?\]/, '')
68
+ # "a:not():enabled" => ["not()", "enabled"]
69
+ ignored_attribute.scan(/:([^:]*)/).flatten
70
+ end
71
+
72
+ # @param selector [String]
73
+ # @return [Boolean]
74
+ # @example
75
+ # multiple_selectors?('a.cls b#id') # => true
76
+ # multiple_selectors?('a.cls') # => false
77
+ def multiple_selectors?(selector)
78
+ selector.match?(/[ >,+]/)
79
+ end
80
+
81
+ # @param value [String]
82
+ # @return [Boolean, String]
83
+ # @example
84
+ # normalize_value('true') # => true
85
+ # normalize_value('false') # => false
86
+ # normalize_value(nil) # => false
87
+ # normalize_value("foo") # => "'foo'"
88
+ def normalize_value(value)
89
+ case value
90
+ when 'true' then true
91
+ when 'false' then false
92
+ when nil then true
93
+ else "'#{value}'"
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -4,7 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  # Helps determine the offending location if there is not an empty line
7
- # following the node. Allows comments to follow directly after.
7
+ # following the node. Allows comments to follow directly after
8
+ # in the following cases.
9
+ # - `rubocop:enable` directive
10
+ # - followed by empty line(s)
8
11
  module EmptyLineSeparation
9
12
  include FinalEndLocation
10
13
  include RangeHelp
@@ -21,13 +24,19 @@ module RuboCop
21
24
  end
22
25
 
23
26
  def missing_separating_line(node)
24
- line = final_end_location(node).line
27
+ line = final_end_line = final_end_location(node).line
25
28
 
26
- line += 1 while comment_line?(processed_source[line])
29
+ while comment_line?(processed_source[line])
30
+ line += 1
31
+ comment = processed_source.comment_at_line(line)
32
+ if DirectiveComment.new(comment).enabled?
33
+ enable_directive_line = line
34
+ end
35
+ end
27
36
 
28
37
  return if processed_source[line].blank?
29
38
 
30
- yield offending_loc(line)
39
+ yield offending_loc(enable_directive_line || final_end_line)
31
40
  end
32
41
 
33
42
  def offending_loc(last_line)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Helps to find namespace of the node.
7
+ module Namespace
8
+ private
9
+
10
+ # @param node [RuboCop::AST::Node]
11
+ # @return [Array<String>]
12
+ # @example
13
+ # namespace(node) # => ['A', 'B', 'C']
14
+ def namespace(node)
15
+ node
16
+ .each_ancestor(:class, :module)
17
+ .reverse_each
18
+ .flat_map { |ancestor| ancestor.defined_module_name.split('::') }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -22,6 +22,7 @@ module RuboCop
22
22
  # describe '.do_something_else' do
23
23
  # end
24
24
  # end
25
+ #
25
26
  class MultipleDescribes < Base
26
27
  include TopLevelGroup
27
28
 
@@ -11,7 +11,6 @@ module RuboCop
11
11
  # and works with `--auto-gen-config`.
12
12
  #
13
13
  # @example
14
- #
15
14
  # # bad
16
15
  # describe UserCreator do
17
16
  # it 'builds a user' do
@@ -31,8 +30,25 @@ module RuboCop
31
30
  # end
32
31
  # end
33
32
  #
34
- # @example configuration
33
+ # @example `aggregate_failures: true` (default)
34
+ # # good - the cop ignores when RSpec aggregates failures
35
+ # describe UserCreator do
36
+ # it 'builds a user', :aggregate_failures do
37
+ # expect(user.name).to eq("John")
38
+ # expect(user.age).to eq(22)
39
+ # end
40
+ # end
35
41
  #
42
+ # @example `aggregate_failures: false`
43
+ # # Detected as an offense
44
+ # describe UserCreator do
45
+ # it 'builds a user', aggregate_failures: false do
46
+ # expect(user.name).to eq("John")
47
+ # expect(user.age).to eq(22)
48
+ # end
49
+ # end
50
+ #
51
+ # @example configuration
36
52
  # # .rubocop.yml
37
53
  # # RSpec/MultipleExpectations:
38
54
  # # Max: 2
@@ -68,7 +84,7 @@ module RuboCop
68
84
  (block (send nil? :aggregate_failures ...) ...)
69
85
  PATTERN
70
86
 
71
- def on_block(node)
87
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
72
88
  return unless example?(node)
73
89
 
74
90
  return if example_with_aggregate_failures?(node)
@@ -56,7 +56,6 @@ module RuboCop
56
56
  # end
57
57
  #
58
58
  # @example when disabling AllowSubject configuration
59
- #
60
59
  # # rubocop.yml
61
60
  # # RSpec/MultipleMemoizedHelpers:
62
61
  # # AllowSubject: false
@@ -72,7 +71,6 @@ module RuboCop
72
71
  # end
73
72
  #
74
73
  # @example with Max configuration
75
- #
76
74
  # # rubocop.yml
77
75
  # # RSpec/MultipleMemoizedHelpers:
78
76
  # # Max: 1
@@ -89,7 +87,7 @@ module RuboCop
89
87
 
90
88
  MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
91
89
 
92
- def on_block(node)
90
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
93
91
  return unless spec_group?(node)
94
92
 
95
93
  count = all_helpers(node).uniq.count