rubocop-rspec 2.11.1 → 2.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 (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