rubocop-rspec 1.44.0 → 2.1.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -1
  3. data/README.md +5 -1
  4. data/config/default.yml +170 -81
  5. data/lib/rubocop-rspec.rb +7 -8
  6. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +7 -3
  7. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +7 -3
  8. data/lib/rubocop/cop/rspec/around_block.rb +1 -1
  9. data/lib/rubocop/cop/rspec/base.rb +6 -55
  10. data/lib/rubocop/cop/rspec/be.rb +2 -2
  11. data/lib/rubocop/cop/rspec/before_after_all.rb +3 -3
  12. data/lib/rubocop/cop/rspec/capybara/current_path_expectation.rb +2 -2
  13. data/lib/rubocop/cop/rspec/capybara/feature_methods.rb +3 -2
  14. data/lib/rubocop/cop/rspec/describe_class.rb +2 -2
  15. data/lib/rubocop/cop/rspec/describe_method.rb +2 -2
  16. data/lib/rubocop/cop/rspec/described_class.rb +1 -2
  17. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +1 -2
  18. data/lib/rubocop/cop/rspec/dialect.rb +1 -1
  19. data/lib/rubocop/cop/rspec/empty_example_group.rb +6 -45
  20. data/lib/rubocop/cop/rspec/empty_hook.rb +1 -1
  21. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +1 -1
  22. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +1 -1
  23. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +1 -1
  24. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +1 -1
  25. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +2 -2
  26. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -1
  27. data/lib/rubocop/cop/rspec/expect_in_hook.rb +1 -1
  28. data/lib/rubocop/cop/rspec/expect_output.rb +1 -1
  29. data/lib/rubocop/cop/rspec/file_path.rb +24 -17
  30. data/lib/rubocop/cop/rspec/focus.rb +43 -8
  31. data/lib/rubocop/cop/rspec/hook_argument.rb +2 -4
  32. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +2 -2
  33. data/lib/rubocop/cop/rspec/implicit_expect.rb +1 -1
  34. data/lib/rubocop/cop/rspec/instance_spy.rb +1 -1
  35. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  36. data/lib/rubocop/cop/rspec/it_behaves_like.rb +1 -1
  37. data/lib/rubocop/cop/rspec/iterated_expectation.rb +1 -1
  38. data/lib/rubocop/cop/rspec/let_before_examples.rb +2 -2
  39. data/lib/rubocop/cop/rspec/let_setup.rb +7 -4
  40. data/lib/rubocop/cop/rspec/message_spies.rb +3 -3
  41. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +51 -0
  42. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  43. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  44. data/lib/rubocop/cop/rspec/mixin/variable.rb +20 -0
  45. data/lib/rubocop/cop/rspec/multiple_describes.rb +2 -3
  46. data/lib/rubocop/cop/rspec/multiple_expectations.rb +1 -1
  47. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +3 -1
  48. data/lib/rubocop/cop/rspec/named_subject.rb +8 -12
  49. data/lib/rubocop/cop/rspec/nested_groups.rb +1 -1
  50. data/lib/rubocop/cop/rspec/overwriting_setup.rb +2 -1
  51. data/lib/rubocop/cop/rspec/pending.rb +13 -5
  52. data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
  53. data/lib/rubocop/cop/rspec/repeated_include_example.rb +3 -2
  54. data/lib/rubocop/cop/rspec/scattered_setup.rb +1 -1
  55. data/lib/rubocop/cop/rspec/shared_context.rb +18 -11
  56. data/lib/rubocop/cop/rspec/shared_examples.rb +3 -1
  57. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +1 -1
  58. data/lib/rubocop/cop/rspec/stubbed_mock.rb +1 -1
  59. data/lib/rubocop/cop/rspec/subject_stub.rb +16 -6
  60. data/lib/rubocop/cop/rspec/variable_definition.rb +1 -1
  61. data/lib/rubocop/cop/rspec/variable_name.rb +1 -1
  62. data/lib/rubocop/cop/rspec_cops.rb +0 -1
  63. data/lib/rubocop/rspec/align_let_brace.rb +1 -1
  64. data/lib/rubocop/rspec/concept.rb +2 -2
  65. data/lib/rubocop/rspec/config_formatter.rb +5 -3
  66. data/lib/rubocop/rspec/corrector/move_node.rb +1 -1
  67. data/lib/rubocop/rspec/example_group.rb +15 -5
  68. data/lib/rubocop/rspec/hook.rb +1 -1
  69. data/lib/rubocop/rspec/inject.rb +4 -2
  70. data/lib/rubocop/rspec/language.rb +143 -109
  71. data/lib/rubocop/rspec/language/node_pattern.rb +7 -24
  72. data/lib/rubocop/rspec/node.rb +1 -1
  73. data/lib/rubocop/rspec/version.rb +1 -1
  74. metadata +13 -17
  75. data/lib/rubocop/cop/rspec/cop.rb +0 -10
  76. data/lib/rubocop/cop/rspec/invalid_predicate_matcher.rb +0 -41
  77. data/lib/rubocop/rspec.rb +0 -12
  78. data/lib/rubocop/rspec/empty_line_separation.rb +0 -48
  79. data/lib/rubocop/rspec/final_end_location.rb +0 -17
  80. data/lib/rubocop/rspec/top_level_describe.rb +0 -52
  81. data/lib/rubocop/rspec/top_level_group.rb +0 -57
  82. data/lib/rubocop/rspec/variable.rb +0 -16
@@ -60,7 +60,7 @@ module RuboCop
60
60
  } ...)
61
61
  PATTERN
62
62
 
63
- def_node_matcher :expect?, Expectations::ALL.send_pattern
63
+ def_node_matcher :expect?, send_pattern('#Expectations.all')
64
64
  def_node_matcher :aggregate_failures_block?, <<-PATTERN
65
65
  (block (send nil? :aggregate_failures ...) ...)
66
66
  PATTERN
@@ -85,7 +85,7 @@ module RuboCop
85
85
  #
86
86
  class MultipleMemoizedHelpers < Base
87
87
  include ConfigurableMax
88
- include RuboCop::RSpec::Variable
88
+ include Variable
89
89
 
90
90
  MSG = 'Example group has too many memoized helpers [%<count>d/%<max>d]'
91
91
 
@@ -101,6 +101,7 @@ module RuboCop
101
101
  end
102
102
 
103
103
  def on_new_investigation
104
+ super
104
105
  @example_group_memoized_helpers = {}
105
106
  end
106
107
 
@@ -128,6 +129,7 @@ module RuboCop
128
129
 
129
130
  def variable_nodes(node)
130
131
  example_group = RuboCop::RSpec::ExampleGroup.new(node)
132
+
131
133
  if allow_subject?
132
134
  example_group.lets
133
135
  else
@@ -42,24 +42,20 @@ module RuboCop
42
42
  # it { is_expected.to be_valid }
43
43
  # end
44
44
  class NamedSubject < Base
45
- MSG = 'Name your test subject if you need '\
46
- 'to reference it explicitly.'
45
+ MSG = 'Name your test subject if you need to reference it explicitly.'
47
46
 
48
- def_node_matcher :rspec_block?, <<-PATTERN
49
- {
50
- #{Examples::ALL.block_pattern}
51
- #{Hooks::ALL.block_pattern}
52
- }
53
- PATTERN
47
+ def_node_matcher :example_or_hook_block?,
48
+ block_pattern('{#Examples.all #Hooks.all}')
54
49
 
55
- def_node_matcher :shared_example?, <<-PATTERN
56
- #{SharedGroups::EXAMPLES.block_pattern}
57
- PATTERN
50
+ def_node_matcher :shared_example?,
51
+ block_pattern('#SharedGroups.examples')
58
52
 
59
53
  def_node_search :subject_usage, '$(send nil? :subject)'
60
54
 
61
55
  def on_block(node)
62
- return if !rspec_block?(node) || ignored_shared_example?(node)
56
+ if !example_or_hook_block?(node) || ignored_shared_example?(node)
57
+ return
58
+ end
63
59
 
64
60
  subject_usage(node) do |subject_node|
65
61
  add_offense(subject_node.loc.selector)
@@ -87,7 +87,7 @@ module RuboCop
87
87
  #
88
88
  class NestedGroups < Base
89
89
  include ConfigurableMax
90
- include RuboCop::RSpec::TopLevelGroup
90
+ include TopLevelGroup
91
91
 
92
92
  MSG = 'Maximum example group nesting exceeded [%<total>d/%<max>d].'
93
93
 
@@ -24,7 +24,8 @@ module RuboCop
24
24
  class OverwritingSetup < Base
25
25
  MSG = '`%<name>s` is already defined.'
26
26
 
27
- def_node_matcher :setup?, (Helpers::ALL + Subject::ALL).block_pattern
27
+ def_node_matcher :setup?, block_pattern('{#Helpers.all #Subjects.all}')
28
+
28
29
  def_node_matcher :first_argument_name, '(send _ _ ({str sym} $_))'
29
30
 
30
31
  def on_block(node)
@@ -34,10 +34,10 @@ module RuboCop
34
34
  class Pending < Base
35
35
  MSG = 'Pending spec found.'
36
36
 
37
- PENDING = Examples::PENDING + Examples::SKIPPED + ExampleGroups::SKIPPED
38
- SKIPPABLE = ExampleGroups::GROUPS + Examples::EXAMPLES
39
-
40
- def_node_matcher :skippable?, SKIPPABLE.send_pattern
37
+ def_node_matcher :skippable?,
38
+ send_pattern(<<~PATTERN)
39
+ {#ExampleGroups.regular #Examples.regular}
40
+ PATTERN
41
41
 
42
42
  def_node_matcher :skipped_in_metadata?, <<-PATTERN
43
43
  {
@@ -47,7 +47,15 @@ module RuboCop
47
47
  PATTERN
48
48
 
49
49
  def_node_matcher :skip_or_pending?, '{(sym :skip) (sym :pending)}'
50
- def_node_matcher :pending_block?, PENDING.send_pattern
50
+
51
+ def_node_matcher :pending_block?,
52
+ send_pattern(<<~PATTERN)
53
+ {
54
+ #ExampleGroups.skipped
55
+ #Examples.skipped
56
+ #Examples.pending
57
+ }
58
+ PATTERN
51
59
 
52
60
  def on_send(node)
53
61
  return unless pending_block?(node) || skipped?(node)
@@ -30,7 +30,7 @@ module RuboCop
30
30
  (send nil? :expect {
31
31
  (block $(send !nil? #predicate? ...) ...)
32
32
  $(send !nil? #predicate? ...)})
33
- $#{Runners::ALL.node_pattern_union}
33
+ $#Runners.all
34
34
  $#boolean_matcher?)
35
35
  PATTERN
36
36
 
@@ -155,7 +155,7 @@ module RuboCop
155
155
  def_node_matcher :predicate_matcher?, <<-PATTERN
156
156
  (send
157
157
  (send nil? :expect $!nil?)
158
- #{Runners::ALL.node_pattern_union}
158
+ #Runners.all
159
159
  {$(send nil? #predicate_matcher_name? ...)
160
160
  (block $(send nil? #predicate_matcher_name? ...) ...)})
161
161
  PATTERN
@@ -164,7 +164,7 @@ module RuboCop
164
164
  (block
165
165
  (send
166
166
  (send nil? :expect $!nil?)
167
- #{Runners::ALL.node_pattern_union}
167
+ #Runners.all
168
168
  $(send nil? #predicate_matcher_name?))
169
169
  ...)
170
170
  PATTERN
@@ -54,10 +54,11 @@ module RuboCop
54
54
  (begin <#include_examples? #include_examples? ...>)
55
55
  PATTERN
56
56
 
57
- def_node_matcher :include_examples?, Includes::EXAMPLES.send_pattern
57
+ def_node_matcher :include_examples?,
58
+ send_pattern('#Includes.examples')
58
59
 
59
60
  def_node_matcher :shared_examples_name, <<-PATTERN
60
- (send _ #{Includes::EXAMPLES.node_pattern_union} $_ ...)
61
+ (send _ #Includes.examples $_ ...)
61
62
  PATTERN
62
63
 
63
64
  def on_begin(node)
@@ -23,7 +23,7 @@ module RuboCop
23
23
  # end
24
24
  #
25
25
  class ScatteredSetup < Base
26
- MSG = 'Do not define multiple `%<hook_name>s` hooks in the same '\
26
+ MSG = 'Do not define multiple `%<hook_name>s` hooks in the same ' \
27
27
  'example group (also defined on %<lines>s).'
28
28
 
29
29
  def on_block(node)
@@ -53,20 +53,27 @@ module RuboCop
53
53
  class SharedContext < Base
54
54
  extend AutoCorrector
55
55
 
56
- MSG_EXAMPLES = "Use `shared_examples` when you don't "\
57
- 'define context.'
56
+ MSG_EXAMPLES = "Use `shared_examples` when you don't define context."
57
+ MSG_CONTEXT = "Use `shared_context` when you don't define examples."
58
58
 
59
- MSG_CONTEXT = "Use `shared_context` when you don't "\
60
- 'define examples.'
59
+ def_node_search :examples?,
60
+ send_pattern('{#Includes.examples #Examples.all}')
61
61
 
62
- examples = (Examples::ALL + Includes::EXAMPLES)
63
- def_node_search :examples?, examples.send_pattern
62
+ def_node_search :context?, <<-PATTERN
63
+ (
64
+ send #rspec? {
65
+ #Subjects.all
66
+ #Helpers.all
67
+ #Includes.context
68
+ #Hooks.all
69
+ } ...
70
+ )
71
+ PATTERN
64
72
 
65
- context = (Hooks::ALL + Helpers::ALL + Includes::CONTEXT + Subject::ALL)
66
- def_node_search :context?, context.send_pattern
67
-
68
- def_node_matcher :shared_context, SharedGroups::CONTEXT.block_pattern
69
- def_node_matcher :shared_example, SharedGroups::EXAMPLES.block_pattern
73
+ def_node_matcher :shared_context,
74
+ block_pattern('#SharedGroups.context')
75
+ def_node_matcher :shared_example,
76
+ block_pattern('#SharedGroups.examples')
70
77
 
71
78
  def on_block(node)
72
79
  context_with_only_examples(node) do
@@ -24,7 +24,9 @@ module RuboCop
24
24
  extend AutoCorrector
25
25
 
26
26
  def_node_matcher :shared_examples,
27
- (SharedGroups::ALL + Includes::ALL).send_pattern
27
+ send_pattern(
28
+ '{#SharedGroups.all #Includes.all}'
29
+ )
28
30
 
29
31
  def on_send(node)
30
32
  shared_examples(node) do
@@ -19,7 +19,7 @@ module RuboCop
19
19
  class SingleArgumentMessageChain < Base
20
20
  extend AutoCorrector
21
21
 
22
- MSG = 'Use `%<recommended>s` instead of calling '\
22
+ MSG = 'Use `%<recommended>s` instead of calling ' \
23
23
  '`%<called>s` with a single argument.'
24
24
 
25
25
  def_node_matcher :message_chain, <<-PATTERN
@@ -60,7 +60,7 @@ module RuboCop
60
60
  # @yield [RuboCop::AST::Node] expectation, method name, matcher
61
61
  def_node_matcher :expectation, <<~PATTERN
62
62
  (send
63
- $(send nil? $#{Expectations::ALL.node_pattern_union} ...)
63
+ $(send nil? $#Expectations.all ...)
64
64
  :to $_)
65
65
  PATTERN
66
66
 
@@ -13,16 +13,26 @@ module RuboCop
13
13
  #
14
14
  # @example
15
15
  # # bad
16
- # describe Foo do
17
- # subject(:bar) { baz }
16
+ # describe Article do
17
+ # subject(:article) { Article.new }
18
18
  #
19
- # before do
20
- # allow(bar).to receive(:qux?).and_return(true)
19
+ # it 'indicates that the author is unknown' do
20
+ # allow(article).to receive(:author).and_return(nil)
21
+ # expect(article.description).to include('by an unknown author')
22
+ # end
23
+ # end
24
+ #
25
+ # # good
26
+ # describe Article do
27
+ # subject(:article) { Article.new(author: nil) }
28
+ #
29
+ # it 'indicates that the author is unknown' do
30
+ # expect(article.description).to include('by an unknown author')
21
31
  # end
22
32
  # end
23
33
  #
24
34
  class SubjectStub < Base
25
- include RuboCop::RSpec::TopLevelGroup
35
+ include TopLevelGroup
26
36
 
27
37
  MSG = 'Do not stub methods of the object under test.'
28
38
 
@@ -66,7 +76,7 @@ module RuboCop
66
76
  (send nil? { :expect :allow } (send nil? {% :subject}))
67
77
  (send nil? :is_expected)
68
78
  }
69
- #{Runners::ALL.node_pattern_union}
79
+ #Runners.all
70
80
  #message_expectation_matcher?
71
81
  )
72
82
  PATTERN
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # let('user_name') { 'Adam' }
25
25
  class VariableDefinition < Base
26
26
  include ConfigurableEnforcedStyle
27
- include RuboCop::RSpec::Variable
27
+ include Variable
28
28
 
29
29
  MSG = 'Use %<style>s for variable names.'
30
30
 
@@ -42,7 +42,7 @@ module RuboCop
42
42
  class VariableName < Base
43
43
  include ConfigurableNaming
44
44
  include IgnoredPattern
45
- include RuboCop::RSpec::Variable
45
+ include Variable
46
46
 
47
47
  MSG = 'Use %<style>s for variable names.'
48
48
 
@@ -52,7 +52,6 @@ require_relative 'rspec/implicit_expect'
52
52
  require_relative 'rspec/implicit_subject'
53
53
  require_relative 'rspec/instance_spy'
54
54
  require_relative 'rspec/instance_variable'
55
- require_relative 'rspec/invalid_predicate_matcher'
56
55
  require_relative 'rspec/it_behaves_like'
57
56
  require_relative 'rspec/iterated_expectation'
58
57
  require_relative 'rspec/leading_subject'
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Shared behavior for aligning braces for single line lets
6
6
  class AlignLetBrace
7
- include RuboCop::RSpec::Language::NodePattern
7
+ include RuboCop::RSpec::Language
8
8
 
9
9
  def initialize(root, token)
10
10
  @root = root
@@ -4,9 +4,9 @@ module RuboCop
4
4
  module RSpec
5
5
  # Wrapper for RSpec DSL methods
6
6
  class Concept
7
+ extend RuboCop::NodePattern::Macros
8
+ extend Language::NodePattern
7
9
  include Language
8
- include Language::NodePattern
9
- extend NodePattern::Macros
10
10
 
11
11
  def initialize(node)
12
12
  @node = node
@@ -6,7 +6,7 @@ module RuboCop
6
6
  module RSpec
7
7
  # Builds a YAML config file from two config hashes
8
8
  class ConfigFormatter
9
- NAMESPACES = /^(RSpec|Capybara|FactoryBot|Rails)/.freeze
9
+ EXTENSION_ROOT_DEPARTMENT = %r{^(RSpec/)}.freeze
10
10
  STYLE_GUIDE_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/'
11
11
 
12
12
  def initialize(config, descriptions)
@@ -15,7 +15,9 @@ module RuboCop
15
15
  end
16
16
 
17
17
  def dump
18
- YAML.dump(unified_config).gsub(NAMESPACES, "\n\\1")
18
+ YAML.dump(unified_config)
19
+ .gsub(EXTENSION_ROOT_DEPARTMENT, "\n\\1")
20
+ .gsub(/^(\s+)- /, '\1 - ')
19
21
  end
20
22
 
21
23
  private
@@ -29,7 +31,7 @@ module RuboCop
29
31
  end
30
32
 
31
33
  def cops
32
- (descriptions.keys | config.keys).grep(NAMESPACES)
34
+ (descriptions.keys | config.keys).grep(EXTENSION_ROOT_DEPARTMENT)
33
35
  end
34
36
 
35
37
  attr_reader :config, :descriptions
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Helper methods to move a node
7
7
  class MoveNode
8
8
  include RuboCop::Cop::RangeHelp
9
- include RuboCop::RSpec::FinalEndLocation
9
+ include RuboCop::Cop::RSpec::FinalEndLocation
10
10
 
11
11
  attr_reader :original, :corrector, :processed_source
12
12
 
@@ -10,9 +10,14 @@ module RuboCop
10
10
  #
11
11
  # Selectors which indicate that we should stop searching
12
12
  #
13
- def_node_matcher :scope_change?, (
14
- ExampleGroups::ALL + SharedGroups::ALL + Includes::ALL
15
- ).block_pattern
13
+ def_node_matcher :scope_change?,
14
+ block_pattern(<<~PATTERN)
15
+ {
16
+ #SharedGroups.all
17
+ #ExampleGroups.all
18
+ #Includes.all
19
+ }
20
+ PATTERN
16
21
 
17
22
  def lets
18
23
  find_all_in_scope(node, :let?)
@@ -23,11 +28,15 @@ module RuboCop
23
28
  end
24
29
 
25
30
  def examples
26
- find_all_in_scope(node, :example?).map(&Example.public_method(:new))
31
+ find_all_in_scope(node, :example?).map do |node|
32
+ Example.new(node)
33
+ end
27
34
  end
28
35
 
29
36
  def hooks
30
- find_all_in_scope(node, :hook?).map(&Hook.public_method(:new))
37
+ find_all_in_scope(node, :hook?).map do |node|
38
+ Hook.new(node)
39
+ end
31
40
  end
32
41
 
33
42
  private
@@ -37,6 +46,7 @@ module RuboCop
37
46
  # Searches node and halts when a scope change is detected
38
47
  #
39
48
  # @param node [RuboCop::AST::Node] node to recursively search
49
+ # @param predicate [Symbol] method to call with node as argument
40
50
  #
41
51
  # @return [Array<RuboCop::AST::Node>] discovered nodes
42
52
  def find_all_in_scope(node, predicate)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  private
45
45
 
46
46
  def valid_scope?(node)
47
- node&.sym_type? && Hooks::Scopes::ALL.include?(node.value)
47
+ node&.sym_type? && Language::HookScopes.all(node.value)
48
48
  end
49
49
 
50
50
  def transform_metadata(meta)
@@ -6,9 +6,11 @@ module RuboCop
6
6
  # bit of our configuration.
7
7
  module Inject
8
8
  def self.defaults!
9
- path = CONFIG_DEFAULT.to_s
9
+ project_root = Pathname.new(__dir__).parent.parent.parent.expand_path
10
+ config_default = project_root.join('config', 'default.yml')
11
+ path = config_default.to_s
10
12
  hash = ConfigLoader.send(:load_yaml_configuration, path)
11
- config = Config.new(hash, path)
13
+ config = RuboCop::Config.new(hash, path)
12
14
  puts "configuration from #{path}" if ConfigLoader.debug?
13
15
  config = ConfigLoader.merge_with_default(config, path)
14
16
  ConfigLoader.instance_variable_set(:@default_configuration, config)
@@ -2,152 +2,186 @@
2
2
 
3
3
  module RuboCop
4
4
  module RSpec
5
- # RSpec public API methods that are commonly used in cops
5
+ # Contains node matchers for common RSpec DSL.
6
+ #
7
+ # RSpec allows for configuring aliases for commonly used DSL elements, e.g.
8
+ # example groups and hooks. It is possible to configure RuboCop RSpec to
9
+ # be able to properly detect these elements in the `RSpec/Language` section
10
+ # of the RuboCop YAML configuration file.
11
+ #
12
+ # In addition to providing useful matchers, this class is responsible for
13
+ # using the configured aliases.
6
14
  module Language
7
- # Set of method selectors
8
- class SelectorSet
9
- def initialize(selectors)
10
- @selectors = selectors.freeze
11
- freeze
12
- end
15
+ extend RuboCop::NodePattern::Macros
16
+ extend NodePattern
13
17
 
14
- def ==(other)
15
- selectors.eql?(other.selectors)
16
- end
18
+ class << self
19
+ attr_accessor :config
20
+ end
17
21
 
18
- def +(other)
19
- self.class.new(selectors + other.selectors)
20
- end
22
+ def_node_matcher :rspec?, '{(const {nil? cbase} :RSpec) nil?}'
21
23
 
22
- def include?(selector)
23
- selectors.include?(selector)
24
- end
24
+ def_node_matcher :example_group?, block_pattern('#ExampleGroups.all')
25
25
 
26
- def block_pattern
27
- "(block #{send_pattern} ...)"
28
- end
26
+ def_node_matcher :shared_group?, block_pattern('#SharedGroups.all')
29
27
 
30
- def block_pass_pattern
31
- "(send #rspec? #{node_pattern_union} _ block_pass)"
32
- end
28
+ def_node_matcher :spec_group?,
29
+ block_pattern('{#SharedGroups.all #ExampleGroups.all}')
33
30
 
34
- def block_or_block_pass_pattern
35
- "{#{block_pattern} #{block_pass_pattern}}"
36
- end
31
+ def_node_matcher :example_group_with_body?, <<-PATTERN
32
+ (block #{send_pattern('#ExampleGroups.all')} args !nil?)
33
+ PATTERN
37
34
 
38
- def send_pattern
39
- "(send #rspec? #{node_pattern_union} ...)"
40
- end
35
+ def_node_matcher :example?, block_pattern('#Examples.all')
41
36
 
42
- def send_or_block_or_block_pass_pattern
43
- "{#{send_pattern} #{block_pattern} #{block_pass_pattern}}"
44
- end
37
+ def_node_matcher :hook?, block_pattern('#Hooks.all')
45
38
 
46
- def node_pattern_union
47
- "{#{node_pattern}}"
48
- end
39
+ def_node_matcher :let?, <<-PATTERN
40
+ {
41
+ #{block_pattern('#Helpers.all')}
42
+ (send #rspec? #Helpers.all _ block_pass)
43
+ }
44
+ PATTERN
49
45
 
50
- def node_pattern
51
- selectors.map(&:inspect).join(' ')
52
- end
46
+ def_node_matcher :include?, <<-PATTERN
47
+ {
48
+ #{send_pattern('#Includes.all')}
49
+ #{block_pattern('#Includes.all')}
50
+ }
51
+ PATTERN
53
52
 
54
- def to_a
55
- selectors
56
- end
53
+ def_node_matcher :subject?, block_pattern('#Subjects.all')
57
54
 
58
- protected
55
+ module ExampleGroups # :nodoc:
56
+ class << self
57
+ def all(element)
58
+ regular(element) ||
59
+ skipped(element) ||
60
+ focused(element)
61
+ end
59
62
 
60
- attr_reader :selectors
61
- end
63
+ def regular(element)
64
+ Language.config['ExampleGroups']['Regular'].include?(element.to_s)
65
+ end
62
66
 
63
- module ExampleGroups
64
- GROUPS = SelectorSet.new(%i[describe context feature example_group])
65
- SKIPPED = SelectorSet.new(%i[xdescribe xcontext xfeature])
66
- FOCUSED = SelectorSet.new(%i[fdescribe fcontext ffeature])
67
+ def focused(element)
68
+ Language.config['ExampleGroups']['Focused'].include?(element.to_s)
69
+ end
67
70
 
68
- ALL = GROUPS + SKIPPED + FOCUSED
71
+ def skipped(element)
72
+ Language.config['ExampleGroups']['Skipped'].include?(element.to_s)
73
+ end
74
+ end
69
75
  end
70
76
 
71
- module SharedGroups
72
- EXAMPLES = SelectorSet.new(%i[shared_examples shared_examples_for])
73
- CONTEXT = SelectorSet.new(%i[shared_context])
77
+ module Examples # :nodoc:
78
+ class << self
79
+ def all(element)
80
+ regular(element) ||
81
+ focused(element) ||
82
+ skipped(element) ||
83
+ pending(element)
84
+ end
85
+
86
+ def regular(element)
87
+ Language.config['Examples']['Regular'].include?(element.to_s)
88
+ end
89
+
90
+ def focused(element)
91
+ Language.config['Examples']['Focused'].include?(element.to_s)
92
+ end
93
+
94
+ def skipped(element)
95
+ Language.config['Examples']['Skipped'].include?(element.to_s)
96
+ end
97
+
98
+ def pending(element)
99
+ Language.config['Examples']['Pending'].include?(element.to_s)
100
+ end
101
+ end
102
+ end
74
103
 
75
- ALL = EXAMPLES + CONTEXT
104
+ module Expectations # :nodoc:
105
+ def self.all(element)
106
+ Language.config['Expectations'].include?(element.to_s)
107
+ end
76
108
  end
77
109
 
78
- module Includes
79
- EXAMPLES = SelectorSet.new(
80
- %i[
81
- it_behaves_like
82
- it_should_behave_like
83
- include_examples
84
- ]
85
- )
86
- CONTEXT = SelectorSet.new(%i[include_context])
87
-
88
- ALL = EXAMPLES + CONTEXT
110
+ module Helpers # :nodoc:
111
+ def self.all(element)
112
+ Language.config['Helpers'].include?(element.to_s)
113
+ end
89
114
  end
90
115
 
91
- module Examples
92
- EXAMPLES = SelectorSet.new(%i[it specify example scenario its])
93
- FOCUSED = SelectorSet.new(%i[fit fspecify fexample fscenario focus])
94
- SKIPPED = SelectorSet.new(%i[xit xspecify xexample xscenario skip])
95
- PENDING = SelectorSet.new(%i[pending])
116
+ module Hooks # :nodoc:
117
+ def self.all(element)
118
+ Language.config['Hooks'].include?(element.to_s)
119
+ end
120
+ end
96
121
 
97
- ALL = EXAMPLES + FOCUSED + SKIPPED + PENDING
122
+ module HookScopes # :nodoc:
123
+ def self.all(element)
124
+ Language.config['HookScopes'].include?(element.to_s)
125
+ end
98
126
  end
99
127
 
100
- module Hooks
101
- ALL = SelectorSet.new(
102
- %i[
103
- prepend_before
104
- before
105
- append_before
106
- around
107
- prepend_after
108
- after
109
- append_after
110
- ]
111
- )
112
-
113
- module Scopes
114
- ALL = SelectorSet.new(
115
- %i[
116
- each
117
- example
118
- context
119
- all
120
- suite
121
- ]
122
- )
128
+ module Includes # :nodoc:
129
+ class << self
130
+ def all(element)
131
+ examples(element) ||
132
+ context(element)
133
+ end
134
+
135
+ def examples(element)
136
+ Language.config['Includes']['Examples'].include?(element.to_s)
137
+ end
138
+
139
+ def context(element)
140
+ Language.config['Includes']['Context'].include?(element.to_s)
141
+ end
123
142
  end
124
143
  end
125
144
 
126
- module Helpers
127
- ALL = SelectorSet.new(%i[let let!])
145
+ module Runners # :nodoc:
146
+ def self.all(element)
147
+ Language.config['Runners'].include?(element.to_s)
148
+ end
128
149
  end
129
150
 
130
- module Subject
131
- ALL = SelectorSet.new(%i[subject subject!])
151
+ module SharedGroups # :nodoc:
152
+ class << self
153
+ def all(element)
154
+ examples(element) ||
155
+ context(element)
156
+ end
157
+
158
+ def examples(element)
159
+ Language.config['SharedGroups']['Examples'].include?(element.to_s)
160
+ end
161
+
162
+ def context(element)
163
+ Language.config['SharedGroups']['Context'].include?(element.to_s)
164
+ end
165
+ end
132
166
  end
133
167
 
134
- module Expectations
135
- ALL = SelectorSet.new(%i[expect is_expected expect_any_instance_of])
168
+ module Subjects # :nodoc:
169
+ def self.all(element)
170
+ Language.config['Subjects'].include?(element.to_s)
171
+ end
136
172
  end
137
173
 
138
- module Runners
139
- ALL = SelectorSet.new(%i[to to_not not_to])
174
+ # This is used in Dialect and DescribeClass cops to detect RSpec blocks.
175
+ module ALL # :nodoc:
176
+ def self.all(element)
177
+ [ExampleGroups, Examples, Expectations, Helpers, Hooks, Includes,
178
+ Runners, SharedGroups, Subjects]
179
+ .find { |concept| concept.all(element) }
180
+ end
140
181
  end
141
182
 
142
- ALL =
143
- ExampleGroups::ALL +
144
- SharedGroups::ALL +
145
- Examples::ALL +
146
- Hooks::ALL +
147
- Helpers::ALL +
148
- Subject::ALL +
149
- Expectations::ALL +
150
- Runners::ALL
183
+ private_constant :ExampleGroups, :Examples, :Expectations, :Hooks,
184
+ :Includes, :Runners, :SharedGroups, :ALL
151
185
  end
152
186
  end
153
187
  end