rubocop-rspec 1.7.0 → 3.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (193) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +955 -79
  3. data/CODE_OF_CONDUCT.md +17 -0
  4. data/MIT-LICENSE.md +1 -2
  5. data/README.md +35 -35
  6. data/config/default.yml +940 -52
  7. data/config/obsoletion.yml +30 -0
  8. data/lib/rubocop/cop/rspec/align_left_let_brace.rb +49 -0
  9. data/lib/rubocop/cop/rspec/align_right_let_brace.rb +49 -0
  10. data/lib/rubocop/cop/rspec/any_instance.rb +10 -13
  11. data/lib/rubocop/cop/rspec/around_block.rb +97 -0
  12. data/lib/rubocop/cop/rspec/base.rb +26 -0
  13. data/lib/rubocop/cop/rspec/be.rb +39 -0
  14. data/lib/rubocop/cop/rspec/be_empty.rb +45 -0
  15. data/lib/rubocop/cop/rspec/be_eq.rb +47 -0
  16. data/lib/rubocop/cop/rspec/be_eql.rb +18 -15
  17. data/lib/rubocop/cop/rspec/be_nil.rb +74 -0
  18. data/lib/rubocop/cop/rspec/before_after_all.rb +45 -0
  19. data/lib/rubocop/cop/rspec/change_by_zero.rb +184 -0
  20. data/lib/rubocop/cop/rspec/class_check.rb +101 -0
  21. data/lib/rubocop/cop/rspec/contain_exactly.rb +56 -0
  22. data/lib/rubocop/cop/rspec/context_method.rb +57 -0
  23. data/lib/rubocop/cop/rspec/context_wording.rb +117 -0
  24. data/lib/rubocop/cop/rspec/describe_class.rb +52 -21
  25. data/lib/rubocop/cop/rspec/describe_method.rb +26 -11
  26. data/lib/rubocop/cop/rspec/describe_symbol.rb +37 -0
  27. data/lib/rubocop/cop/rspec/described_class.rb +181 -34
  28. data/lib/rubocop/cop/rspec/described_class_module_wrapping.rb +38 -0
  29. data/lib/rubocop/cop/rspec/dialect.rb +84 -0
  30. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +58 -0
  31. data/lib/rubocop/cop/rspec/empty_example_group.rb +134 -47
  32. data/lib/rubocop/cop/rspec/empty_hook.rb +49 -0
  33. data/lib/rubocop/cop/rspec/empty_line_after_example.rb +82 -0
  34. data/lib/rubocop/cop/rspec/empty_line_after_example_group.rb +42 -0
  35. data/lib/rubocop/cop/rspec/empty_line_after_final_let.rb +40 -0
  36. data/lib/rubocop/cop/rspec/empty_line_after_hook.rb +82 -0
  37. data/lib/rubocop/cop/rspec/empty_line_after_subject.rb +36 -0
  38. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  39. data/lib/rubocop/cop/rspec/empty_output.rb +47 -0
  40. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  41. data/lib/rubocop/cop/rspec/example_length.rb +38 -20
  42. data/lib/rubocop/cop/rspec/example_without_description.rb +98 -0
  43. data/lib/rubocop/cop/rspec/example_wording.rb +117 -27
  44. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +110 -0
  45. data/lib/rubocop/cop/rspec/expect_actual.rb +46 -20
  46. data/lib/rubocop/cop/rspec/expect_change.rb +86 -0
  47. data/lib/rubocop/cop/rspec/expect_in_hook.rb +50 -0
  48. data/lib/rubocop/cop/rspec/expect_in_let.rb +42 -0
  49. data/lib/rubocop/cop/rspec/expect_output.rb +50 -0
  50. data/lib/rubocop/cop/rspec/focus.rb +79 -25
  51. data/lib/rubocop/cop/rspec/hook_argument.rb +48 -36
  52. data/lib/rubocop/cop/rspec/hooks_before_examples.rb +81 -0
  53. data/lib/rubocop/cop/rspec/identical_equality_assertion.rb +37 -0
  54. data/lib/rubocop/cop/rspec/implicit_block_expectation.rb +68 -0
  55. data/lib/rubocop/cop/rspec/implicit_expect.rb +100 -0
  56. data/lib/rubocop/cop/rspec/implicit_subject.rb +167 -0
  57. data/lib/rubocop/cop/rspec/indexed_let.rb +112 -0
  58. data/lib/rubocop/cop/rspec/instance_spy.rb +74 -0
  59. data/lib/rubocop/cop/rspec/instance_variable.rb +28 -14
  60. data/lib/rubocop/cop/rspec/is_expected_specify.rb +45 -0
  61. data/lib/rubocop/cop/rspec/it_behaves_like.rb +49 -0
  62. data/lib/rubocop/cop/rspec/iterated_expectation.rb +74 -0
  63. data/lib/rubocop/cop/rspec/leading_subject.rb +57 -29
  64. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +127 -0
  65. data/lib/rubocop/cop/rspec/let_before_examples.rb +101 -0
  66. data/lib/rubocop/cop/rspec/let_setup.rb +32 -16
  67. data/lib/rubocop/cop/rspec/match_array.rb +59 -0
  68. data/lib/rubocop/cop/rspec/message_chain.rb +10 -15
  69. data/lib/rubocop/cop/rspec/message_expectation.rb +12 -9
  70. data/lib/rubocop/cop/rspec/message_spies.rb +88 -0
  71. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  72. data/lib/rubocop/cop/rspec/missing_example_group_argument.rb +35 -0
  73. data/lib/rubocop/cop/rspec/missing_expectation_target_method.rb +54 -0
  74. data/lib/rubocop/cop/rspec/mixin/comments_help.rb +38 -0
  75. data/lib/rubocop/cop/rspec/mixin/empty_line_separation.rb +59 -0
  76. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  77. data/lib/rubocop/cop/rspec/mixin/final_end_location.rb +19 -0
  78. data/lib/rubocop/cop/rspec/mixin/inside_example_group.rb +29 -0
  79. data/lib/rubocop/cop/rspec/mixin/location_help.rb +37 -0
  80. data/lib/rubocop/cop/rspec/mixin/metadata.rb +63 -0
  81. data/lib/rubocop/cop/rspec/mixin/namespace.rb +23 -0
  82. data/lib/rubocop/cop/rspec/mixin/skip_or_pending.rb +39 -0
  83. data/lib/rubocop/cop/rspec/mixin/top_level_group.rb +54 -0
  84. data/lib/rubocop/cop/rspec/mixin/variable.rb +21 -0
  85. data/lib/rubocop/cop/rspec/multiple_describes.rb +14 -12
  86. data/lib/rubocop/cop/rspec/multiple_expectations.rb +86 -26
  87. data/lib/rubocop/cop/rspec/multiple_memoized_helpers.rb +146 -0
  88. data/lib/rubocop/cop/rspec/multiple_subjects.rb +97 -0
  89. data/lib/rubocop/cop/rspec/named_subject.rb +107 -27
  90. data/lib/rubocop/cop/rspec/nested_groups.rb +84 -47
  91. data/lib/rubocop/cop/rspec/no_expectation_example.rb +102 -0
  92. data/lib/rubocop/cop/rspec/not_to_not.rb +30 -27
  93. data/lib/rubocop/cop/rspec/overwriting_setup.rb +74 -0
  94. data/lib/rubocop/cop/rspec/pending.rb +80 -0
  95. data/lib/rubocop/cop/rspec/pending_without_reason.rb +159 -0
  96. data/lib/rubocop/cop/rspec/predicate_matcher.rb +341 -0
  97. data/lib/rubocop/cop/rspec/receive_counts.rb +89 -0
  98. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  99. data/lib/rubocop/cop/rspec/receive_never.rb +41 -0
  100. data/lib/rubocop/cop/rspec/redundant_around.rb +65 -0
  101. data/lib/rubocop/cop/rspec/redundant_predicate_matcher.rb +67 -0
  102. data/lib/rubocop/cop/rspec/remove_const.rb +39 -0
  103. data/lib/rubocop/cop/rspec/repeated_description.rb +98 -0
  104. data/lib/rubocop/cop/rspec/repeated_example.rb +53 -0
  105. data/lib/rubocop/cop/rspec/repeated_example_group_body.rb +100 -0
  106. data/lib/rubocop/cop/rspec/repeated_example_group_description.rb +96 -0
  107. data/lib/rubocop/cop/rspec/repeated_include_example.rb +105 -0
  108. data/lib/rubocop/cop/rspec/repeated_subject_call.rb +125 -0
  109. data/lib/rubocop/cop/rspec/return_from_stub.rb +169 -0
  110. data/lib/rubocop/cop/rspec/scattered_let.rb +59 -0
  111. data/lib/rubocop/cop/rspec/scattered_setup.rb +92 -0
  112. data/lib/rubocop/cop/rspec/shared_context.rb +107 -0
  113. data/lib/rubocop/cop/rspec/shared_examples.rb +125 -0
  114. data/lib/rubocop/cop/rspec/single_argument_message_chain.rb +93 -0
  115. data/lib/rubocop/cop/rspec/skip_block_inside_example.rb +46 -0
  116. data/lib/rubocop/cop/rspec/sort_metadata.rb +71 -0
  117. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  118. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  119. data/lib/rubocop/cop/rspec/stubbed_mock.rb +176 -0
  120. data/lib/rubocop/cop/rspec/subject_declaration.rb +46 -0
  121. data/lib/rubocop/cop/rspec/subject_stub.rb +93 -74
  122. data/lib/rubocop/cop/rspec/undescriptive_literals_description.rb +69 -0
  123. data/lib/rubocop/cop/rspec/unspecified_exception.rb +67 -0
  124. data/lib/rubocop/cop/rspec/variable_definition.rb +77 -0
  125. data/lib/rubocop/cop/rspec/variable_name.rb +68 -0
  126. data/lib/rubocop/cop/rspec/verified_double_reference.rb +111 -0
  127. data/lib/rubocop/cop/rspec/verified_doubles.rb +28 -14
  128. data/lib/rubocop/cop/rspec/void_expect.rb +60 -0
  129. data/lib/rubocop/cop/rspec/yield.rb +82 -0
  130. data/lib/rubocop/cop/rspec_cops.rb +112 -0
  131. data/lib/rubocop/rspec/align_let_brace.rb +63 -0
  132. data/lib/rubocop/rspec/concept.rb +33 -0
  133. data/lib/rubocop/rspec/config_formatter.rb +27 -4
  134. data/lib/rubocop/rspec/cop/generator.rb +25 -0
  135. data/lib/rubocop/rspec/corrector/move_node.rb +51 -0
  136. data/lib/rubocop/rspec/description_extractor.rb +60 -18
  137. data/lib/rubocop/rspec/example.rb +37 -0
  138. data/lib/rubocop/rspec/example_group.rb +67 -0
  139. data/lib/rubocop/rspec/hook.rb +79 -0
  140. data/lib/rubocop/rspec/inject.rb +3 -1
  141. data/lib/rubocop/rspec/language.rb +184 -41
  142. data/lib/rubocop/rspec/node.rb +19 -0
  143. data/lib/rubocop/rspec/shared_contexts/default_rspec_language_config_context.rb +29 -0
  144. data/lib/rubocop/rspec/version.rb +1 -1
  145. data/lib/rubocop/rspec/wording.rb +61 -19
  146. data/lib/rubocop/rspec.rb +6 -2
  147. data/lib/rubocop-rspec.rb +45 -34
  148. metadata +130 -195
  149. data/Gemfile +0 -13
  150. data/Rakefile +0 -48
  151. data/lib/rubocop/cop/rspec/file_path.rb +0 -83
  152. data/lib/rubocop/rspec/language/node_pattern.rb +0 -16
  153. data/lib/rubocop/rspec/spec_only.rb +0 -61
  154. data/lib/rubocop/rspec/top_level_describe.rb +0 -61
  155. data/lib/rubocop/rspec/util.rb +0 -19
  156. data/rubocop-rspec.gemspec +0 -42
  157. data/spec/expect_violation/expectation_spec.rb +0 -85
  158. data/spec/project/changelog_spec.rb +0 -81
  159. data/spec/project/default_config_spec.rb +0 -52
  160. data/spec/project/project_requires_spec.rb +0 -8
  161. data/spec/rubocop/cop/rspec/any_instance_spec.rb +0 -30
  162. data/spec/rubocop/cop/rspec/be_eql_spec.rb +0 -59
  163. data/spec/rubocop/cop/rspec/describe_class_spec.rb +0 -113
  164. data/spec/rubocop/cop/rspec/describe_method_spec.rb +0 -32
  165. data/spec/rubocop/cop/rspec/described_class_spec.rb +0 -219
  166. data/spec/rubocop/cop/rspec/empty_example_group_spec.rb +0 -79
  167. data/spec/rubocop/cop/rspec/example_length_spec.rb +0 -117
  168. data/spec/rubocop/cop/rspec/example_wording_spec.rb +0 -82
  169. data/spec/rubocop/cop/rspec/expect_actual_spec.rb +0 -136
  170. data/spec/rubocop/cop/rspec/file_path_spec.rb +0 -236
  171. data/spec/rubocop/cop/rspec/focus_spec.rb +0 -130
  172. data/spec/rubocop/cop/rspec/hook_argument_spec.rb +0 -189
  173. data/spec/rubocop/cop/rspec/instance_variable_spec.rb +0 -75
  174. data/spec/rubocop/cop/rspec/leading_subject_spec.rb +0 -54
  175. data/spec/rubocop/cop/rspec/let_setup_spec.rb +0 -66
  176. data/spec/rubocop/cop/rspec/message_chain_spec.rb +0 -21
  177. data/spec/rubocop/cop/rspec/message_expectation_spec.rb +0 -63
  178. data/spec/rubocop/cop/rspec/multiple_describes_spec.rb +0 -28
  179. data/spec/rubocop/cop/rspec/multiple_expectations_spec.rb +0 -84
  180. data/spec/rubocop/cop/rspec/named_subject_spec.rb +0 -62
  181. data/spec/rubocop/cop/rspec/nested_groups_spec.rb +0 -55
  182. data/spec/rubocop/cop/rspec/not_to_not_spec.rb +0 -57
  183. data/spec/rubocop/cop/rspec/subject_stub_spec.rb +0 -183
  184. data/spec/rubocop/cop/rspec/verified_doubles_spec.rb +0 -71
  185. data/spec/rubocop/rspec/config_formatter_spec.rb +0 -48
  186. data/spec/rubocop/rspec/description_extractor_spec.rb +0 -35
  187. data/spec/rubocop/rspec/language/selector_set_spec.rb +0 -29
  188. data/spec/rubocop/rspec/spec_only_spec.rb +0 -97
  189. data/spec/rubocop/rspec/util/one_spec.rb +0 -21
  190. data/spec/rubocop/rspec/wording_spec.rb +0 -44
  191. data/spec/shared/rspec_only_cop_behavior.rb +0 -68
  192. data/spec/spec_helper.rb +0 -41
  193. data/spec/support/expect_violation.rb +0 -166
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rspec/align_left_let_brace'
4
+ require_relative 'rspec/align_right_let_brace'
5
+ require_relative 'rspec/any_instance'
6
+ require_relative 'rspec/around_block'
7
+ require_relative 'rspec/be'
8
+ require_relative 'rspec/be_empty'
9
+ require_relative 'rspec/be_eq'
10
+ require_relative 'rspec/be_eql'
11
+ require_relative 'rspec/be_nil'
12
+ require_relative 'rspec/before_after_all'
13
+ require_relative 'rspec/change_by_zero'
14
+ require_relative 'rspec/class_check'
15
+ require_relative 'rspec/contain_exactly'
16
+ require_relative 'rspec/context_method'
17
+ require_relative 'rspec/context_wording'
18
+ require_relative 'rspec/describe_class'
19
+ require_relative 'rspec/describe_method'
20
+ require_relative 'rspec/describe_symbol'
21
+ require_relative 'rspec/described_class'
22
+ require_relative 'rspec/described_class_module_wrapping'
23
+ require_relative 'rspec/dialect'
24
+ require_relative 'rspec/duplicated_metadata'
25
+ require_relative 'rspec/empty_example_group'
26
+ require_relative 'rspec/empty_hook'
27
+ require_relative 'rspec/empty_line_after_example'
28
+ require_relative 'rspec/empty_line_after_example_group'
29
+ require_relative 'rspec/empty_line_after_final_let'
30
+ require_relative 'rspec/empty_line_after_hook'
31
+ require_relative 'rspec/empty_line_after_subject'
32
+ require_relative 'rspec/empty_metadata'
33
+ require_relative 'rspec/empty_output'
34
+ require_relative 'rspec/eq'
35
+ require_relative 'rspec/example_length'
36
+ require_relative 'rspec/example_without_description'
37
+ require_relative 'rspec/example_wording'
38
+ require_relative 'rspec/excessive_docstring_spacing'
39
+ require_relative 'rspec/expect_actual'
40
+ require_relative 'rspec/expect_change'
41
+ require_relative 'rspec/expect_in_hook'
42
+ require_relative 'rspec/expect_in_let'
43
+ require_relative 'rspec/expect_output'
44
+ require_relative 'rspec/focus'
45
+ require_relative 'rspec/hook_argument'
46
+ require_relative 'rspec/hooks_before_examples'
47
+ require_relative 'rspec/identical_equality_assertion'
48
+ require_relative 'rspec/implicit_block_expectation'
49
+ require_relative 'rspec/implicit_expect'
50
+ require_relative 'rspec/implicit_subject'
51
+ require_relative 'rspec/indexed_let'
52
+ require_relative 'rspec/instance_spy'
53
+ require_relative 'rspec/instance_variable'
54
+ require_relative 'rspec/is_expected_specify'
55
+ require_relative 'rspec/it_behaves_like'
56
+ require_relative 'rspec/iterated_expectation'
57
+ require_relative 'rspec/leading_subject'
58
+ require_relative 'rspec/leaky_constant_declaration'
59
+ require_relative 'rspec/let_before_examples'
60
+ require_relative 'rspec/let_setup'
61
+ require_relative 'rspec/match_array'
62
+ require_relative 'rspec/message_chain'
63
+ require_relative 'rspec/message_expectation'
64
+ require_relative 'rspec/message_spies'
65
+ require_relative 'rspec/metadata_style'
66
+ require_relative 'rspec/missing_example_group_argument'
67
+ require_relative 'rspec/missing_expectation_target_method'
68
+ require_relative 'rspec/multiple_describes'
69
+ require_relative 'rspec/multiple_expectations'
70
+ require_relative 'rspec/multiple_memoized_helpers'
71
+ require_relative 'rspec/multiple_subjects'
72
+ require_relative 'rspec/named_subject'
73
+ require_relative 'rspec/nested_groups'
74
+ require_relative 'rspec/no_expectation_example'
75
+ require_relative 'rspec/not_to_not'
76
+ require_relative 'rspec/overwriting_setup'
77
+ require_relative 'rspec/pending'
78
+ require_relative 'rspec/pending_without_reason'
79
+ require_relative 'rspec/predicate_matcher'
80
+ require_relative 'rspec/receive_counts'
81
+ require_relative 'rspec/receive_messages'
82
+ require_relative 'rspec/receive_never'
83
+ require_relative 'rspec/redundant_around'
84
+ require_relative 'rspec/redundant_predicate_matcher'
85
+ require_relative 'rspec/remove_const'
86
+ require_relative 'rspec/repeated_description'
87
+ require_relative 'rspec/repeated_example'
88
+ require_relative 'rspec/repeated_example_group_body'
89
+ require_relative 'rspec/repeated_example_group_description'
90
+ require_relative 'rspec/repeated_include_example'
91
+ require_relative 'rspec/repeated_subject_call'
92
+ require_relative 'rspec/return_from_stub'
93
+ require_relative 'rspec/scattered_let'
94
+ require_relative 'rspec/scattered_setup'
95
+ require_relative 'rspec/shared_context'
96
+ require_relative 'rspec/shared_examples'
97
+ require_relative 'rspec/single_argument_message_chain'
98
+ require_relative 'rspec/skip_block_inside_example'
99
+ require_relative 'rspec/sort_metadata'
100
+ require_relative 'rspec/spec_file_path_format'
101
+ require_relative 'rspec/spec_file_path_suffix'
102
+ require_relative 'rspec/stubbed_mock'
103
+ require_relative 'rspec/subject_declaration'
104
+ require_relative 'rspec/subject_stub'
105
+ require_relative 'rspec/undescriptive_literals_description'
106
+ require_relative 'rspec/unspecified_exception'
107
+ require_relative 'rspec/variable_definition'
108
+ require_relative 'rspec/variable_name'
109
+ require_relative 'rspec/verified_double_reference'
110
+ require_relative 'rspec/verified_doubles'
111
+ require_relative 'rspec/void_expect'
112
+ require_relative 'rspec/yield'
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Shared behavior for aligning braces for single line lets
6
+ class AlignLetBrace
7
+ include RuboCop::RSpec::Language
8
+ include RuboCop::Cop::Util
9
+
10
+ def initialize(root, token)
11
+ @root = root
12
+ @token = token
13
+ end
14
+
15
+ def offending_tokens
16
+ single_line_lets.reject do |let|
17
+ target_column_for(let) == let_token(let).column
18
+ end
19
+ end
20
+
21
+ def indent_for(node)
22
+ ' ' * (target_column_for(node) - let_token(node).column)
23
+ end
24
+
25
+ private
26
+
27
+ def let_token(node)
28
+ node.loc.public_send(token)
29
+ end
30
+
31
+ def target_column_for(let)
32
+ let_group_for(let).map { |member| let_token(member).column }.max
33
+ end
34
+
35
+ def let_group_for(let)
36
+ adjacent_let_chunks.detect do |chunk|
37
+ chunk.any? do |member|
38
+ member == let && same_line?(member, let)
39
+ end
40
+ end
41
+ end
42
+
43
+ def adjacent_let_chunks
44
+ last_line = nil
45
+
46
+ single_line_lets.chunk do |node|
47
+ line = node.loc.line
48
+ last_line = (line if last_line.nil? || last_line + 1 == line)
49
+ last_line.nil?
50
+ end.map(&:last)
51
+ end
52
+
53
+ def single_line_lets
54
+ @single_line_lets ||=
55
+ root.each_node(:block).select do |node|
56
+ let?(node) && node.single_line?
57
+ end
58
+ end
59
+
60
+ attr_reader :root, :token
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec DSL methods
6
+ class Concept
7
+ extend RuboCop::NodePattern::Macros
8
+ include Language
9
+
10
+ def initialize(node)
11
+ @node = node
12
+ end
13
+
14
+ def eql?(other)
15
+ node == other.node
16
+ end
17
+
18
+ alias == eql?
19
+
20
+ def hash
21
+ [self.class, node].hash
22
+ end
23
+
24
+ def to_node
25
+ node
26
+ end
27
+
28
+ protected
29
+
30
+ attr_reader :node
31
+ end
32
+ end
33
+ end
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module RuboCop
4
6
  module RSpec
5
7
  # Builds a YAML config file from two config hashes
6
8
  class ConfigFormatter
7
- NAMESPACE = 'RSpec'.freeze
9
+ EXTENSION_ROOT_DEPARTMENT = %r{^(RSpec/)}.freeze
10
+ SUBDEPARTMENTS = [].freeze
11
+ AMENDMENTS = %(Metrics/BlockLength)
12
+ COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/'
8
13
 
9
14
  def initialize(config, descriptions)
10
15
  @config = config
@@ -12,19 +17,37 @@ module RuboCop
12
17
  end
13
18
 
14
19
  def dump
15
- YAML.dump(unified_config).gsub(/^#{NAMESPACE}/, "\n#{NAMESPACE}")
20
+ YAML.dump(unified_config)
21
+ .gsub(EXTENSION_ROOT_DEPARTMENT, "\n\\1")
22
+ .gsub(*AMENDMENTS, "\n\\0")
23
+ .gsub(/^(\s+)- /, '\1 - ')
24
+ .gsub('"~"', '~')
16
25
  end
17
26
 
18
27
  private
19
28
 
20
29
  def unified_config
21
30
  cops.each_with_object(config.dup) do |cop, unified|
22
- unified[cop] = config.fetch(cop).merge(descriptions.fetch(cop))
31
+ next if SUBDEPARTMENTS.include?(cop) || AMENDMENTS.include?(cop)
32
+
33
+ replace_nil(unified[cop])
34
+ unified[cop].merge!(descriptions.fetch(cop))
35
+ unified[cop]['Reference'] = reference(cop)
23
36
  end
24
37
  end
25
38
 
26
39
  def cops
27
- (descriptions.keys + config.keys).uniq.grep(/\A#{NAMESPACE}/)
40
+ (descriptions.keys | config.keys).grep(EXTENSION_ROOT_DEPARTMENT)
41
+ end
42
+
43
+ def replace_nil(config)
44
+ config.each do |key, value|
45
+ config[key] = '~' if value.nil?
46
+ end
47
+ end
48
+
49
+ def reference(cop)
50
+ COP_DOC_BASE_URL + cop
28
51
  end
29
52
 
30
53
  attr_reader :config, :descriptions
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ module Cop
6
+ # Source and spec generator for new cops
7
+ #
8
+ # This generator will take a cop name and generate a source file
9
+ # and spec file when given a valid qualified cop name.
10
+ # @api private
11
+ class Generator < RuboCop::Cop::Generator
12
+ def todo
13
+ <<~TODO
14
+ Do 4 steps:
15
+ 1. Modify the description of #{badge} in config/default.yml
16
+ 2. Implement your new cop in the generated file!
17
+ 3. Add an entry about new cop to CHANGELOG.md
18
+ 4. Commit your new cop with a message such as
19
+ e.g. "Add new `#{badge}` cop"
20
+ TODO
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ module Corrector
6
+ # Helper methods to move a node
7
+ class MoveNode
8
+ include RuboCop::Cop::RangeHelp
9
+ include RuboCop::Cop::RSpec::CommentsHelp
10
+ include RuboCop::Cop::RSpec::FinalEndLocation
11
+
12
+ attr_reader :original, :corrector, :processed_source
13
+
14
+ def initialize(node, corrector, processed_source)
15
+ @original = node
16
+ @corrector = corrector
17
+ @processed_source = processed_source # used by RangeHelp
18
+ end
19
+
20
+ def move_before(other)
21
+ position = start_line_position(other)
22
+
23
+ corrector.insert_before(position, "#{source(original)}\n")
24
+ corrector.remove(node_range_with_surrounding_space(original))
25
+ end
26
+
27
+ def move_after(other)
28
+ position = end_line_position(other)
29
+
30
+ corrector.insert_after(position, "\n#{source(original)}")
31
+ corrector.remove(node_range_with_surrounding_space(original))
32
+ end
33
+
34
+ private
35
+
36
+ def source(node)
37
+ node_range(node).source
38
+ end
39
+
40
+ def node_range(node)
41
+ source_range_with_comment(node)
42
+ end
43
+
44
+ def node_range_with_surrounding_space(node)
45
+ range = node_range(node)
46
+ range_by_whole_lines(range, include_final_newline: true)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,35 +1,77 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module RSpec
3
5
  # Extracts cop descriptions from YARD docstrings
4
6
  class DescriptionExtractor
5
- COP_NAMESPACE = 'RuboCop::Cop::RSpec'.freeze
6
- COP_FORMAT = 'RSpec/%s'.freeze
7
-
8
7
  def initialize(yardocs)
9
- @yardocs = yardocs
8
+ @code_objects = yardocs.map(&CodeObject.public_method(:new))
10
9
  end
11
10
 
12
11
  def to_h
13
- cop_documentation.each_with_object({}) do |(name, docstring), config|
14
- config[format(COP_FORMAT, name)] = {
15
- 'Description' => docstring.split("\n\n").first.to_s
16
- }
17
- end
12
+ code_objects
13
+ .select(&:rspec_cop?)
14
+ .map(&:configuration)
15
+ .reduce(:merge)
18
16
  end
19
17
 
20
18
  private
21
19
 
22
- def cop_documentation
23
- yardocs
24
- .select(&method(:cop?))
25
- .map { |doc| [doc.name, doc.docstring] }
26
- end
20
+ attr_reader :code_objects
27
21
 
28
- def cop?(doc)
29
- doc.type.equal?(:class) && doc.to_s.start_with?(COP_NAMESPACE)
30
- end
22
+ # Decorator of a YARD code object for working with documented rspec cops
23
+ class CodeObject
24
+ RSPEC_COP_CLASS_NAME = 'RuboCop::Cop::RSpec::Base'
25
+ RUBOCOP_COP_CLASS_NAME = 'RuboCop::Cop::Base'
26
+ RSPEC_NAMESPACE = 'RuboCop::Cop::RSpec'
27
+
28
+ def initialize(yardoc)
29
+ @yardoc = yardoc
30
+ end
31
31
 
32
- attr_reader :yardocs
32
+ # Test if the YARD code object documents a concrete rspec cop class
33
+ #
34
+ # @return [Boolean]
35
+ def rspec_cop?
36
+ cop_subclass? && !abstract? && rspec_cop_namespace?
37
+ end
38
+
39
+ # Configuration for the documented cop that would live in default.yml
40
+ #
41
+ # @return [Hash]
42
+ def configuration
43
+ { cop_name => { 'Description' => description } }
44
+ end
45
+
46
+ private
47
+
48
+ def cop_name
49
+ Object.const_get(documented_constant).cop_name
50
+ end
51
+
52
+ def description
53
+ yardoc.docstring.split("\n\n").first.to_s
54
+ end
55
+
56
+ def rspec_cop_namespace?
57
+ documented_constant.start_with?(RSPEC_NAMESPACE)
58
+ end
59
+
60
+ def documented_constant
61
+ yardoc.to_s
62
+ end
63
+
64
+ def cop_subclass?
65
+ yardoc.superclass.path == RSPEC_COP_CLASS_NAME ||
66
+ yardoc.superclass.path == RUBOCOP_COP_CLASS_NAME
67
+ end
68
+
69
+ def abstract?
70
+ yardoc.tags.any? { |tag| tag.tag_name.eql?('abstract') }
71
+ end
72
+
73
+ attr_reader :yardoc
74
+ end
33
75
  end
34
76
  end
35
77
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec examples
6
+ class Example < Concept
7
+ # @!method extract_doc_string(node)
8
+ def_node_matcher :extract_doc_string, '(send _ _ $_ ...)'
9
+
10
+ # @!method extract_metadata(node)
11
+ def_node_matcher :extract_metadata, '(send _ _ _ $...)'
12
+
13
+ # @!method extract_implementation(node)
14
+ def_node_matcher :extract_implementation, '(block send args $_)'
15
+
16
+ def doc_string
17
+ extract_doc_string(definition)
18
+ end
19
+
20
+ def metadata
21
+ extract_metadata(definition)
22
+ end
23
+
24
+ def implementation
25
+ extract_implementation(node)
26
+ end
27
+
28
+ def definition
29
+ if node.send_type?
30
+ node
31
+ else
32
+ node.send_node
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec example groups
6
+ class ExampleGroup < Concept
7
+ # @!method scope_change?(node)
8
+ #
9
+ # Detect if the node is an example group or shared example
10
+ #
11
+ # Selectors which indicate that we should stop searching
12
+ #
13
+ def_node_matcher :scope_change?, <<~PATTERN
14
+ (block {
15
+ (send #rspec? {#SharedGroups.all #ExampleGroups.all} ...)
16
+ (send nil? #Includes.all ...)
17
+ } ...)
18
+ PATTERN
19
+
20
+ def lets
21
+ find_all_in_scope(node, :let?)
22
+ end
23
+
24
+ def subjects
25
+ find_all_in_scope(node, :subject?)
26
+ end
27
+
28
+ def examples
29
+ find_all_in_scope(node, :example?).map do |node|
30
+ Example.new(node)
31
+ end
32
+ end
33
+
34
+ def hooks
35
+ find_all_in_scope(node, :hook?).map do |node|
36
+ Hook.new(node)
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ # Recursively search for predicate within the current scope
43
+ #
44
+ # Searches node and halts when a scope change is detected
45
+ #
46
+ # @param node [RuboCop::AST::Node] node to recursively search
47
+ # @param predicate [Symbol] method to call with node as argument
48
+ #
49
+ # @return [Array<RuboCop::AST::Node>] discovered nodes
50
+ def find_all_in_scope(node, predicate)
51
+ node.each_child_node.flat_map do |child|
52
+ find_all(child, predicate)
53
+ end
54
+ end
55
+
56
+ def find_all(node, predicate)
57
+ if public_send(predicate, node)
58
+ [node]
59
+ elsif scope_change?(node) || example?(node)
60
+ []
61
+ else
62
+ find_all_in_scope(node, predicate)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module RSpec
5
+ # Wrapper for RSpec hook
6
+ class Hook < Concept
7
+ # @!method extract_metadata(node)
8
+ def_node_matcher :extract_metadata, <<~PATTERN
9
+ (block
10
+ (send _ _ #valid_scope? ? $...) ...
11
+ )
12
+ PATTERN
13
+
14
+ def name
15
+ node.method_name
16
+ end
17
+
18
+ def knowable_scope?
19
+ scope_argument.nil? ||
20
+ scope_argument.sym_type? ||
21
+ scope_argument.hash_type?
22
+ end
23
+
24
+ def example?
25
+ scope.equal?(:each)
26
+ end
27
+
28
+ def scope
29
+ return :each if scope_argument&.hash_type?
30
+
31
+ case scope_name
32
+ when nil, :each, :example then :each
33
+ when :context, :all then :context
34
+ when :suite then :suite
35
+ end
36
+ end
37
+
38
+ def metadata
39
+ (extract_metadata(node) || [])
40
+ .map { |meta| transform_metadata(meta) }
41
+ .flatten
42
+ .inject(&:merge)
43
+ end
44
+
45
+ private
46
+
47
+ def valid_scope?(node)
48
+ node&.sym_type? && Language::HookScopes.all(node.value)
49
+ end
50
+
51
+ def transform_metadata(meta)
52
+ if meta.sym_type?
53
+ { meta => true }
54
+ else
55
+ # This check is to be able to compare those two hooks:
56
+ #
57
+ # before(:example, :special) { ... }
58
+ # before(:example, special: true) { ... }
59
+ #
60
+ # In the second case it's a node with a pair that has a value
61
+ # of a `true_type?`.
62
+ meta.pairs.map { |pair| { pair.key => transform_true(pair.value) } }
63
+ end
64
+ end
65
+
66
+ def transform_true(node)
67
+ node.true_type? ? true : node
68
+ end
69
+
70
+ def scope_name
71
+ scope_argument.to_a.first
72
+ end
73
+
74
+ def scope_argument
75
+ node.send_node.first_argument
76
+ end
77
+ end
78
+ end
79
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RuboCop
2
4
  module RSpec
3
5
  # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
@@ -6,7 +8,7 @@ module RuboCop
6
8
  def self.defaults!
7
9
  path = CONFIG_DEFAULT.to_s
8
10
  hash = ConfigLoader.send(:load_yaml_configuration, path)
9
- config = Config.new(hash, path)
11
+ config = RuboCop::Config.new(hash, path)
10
12
  puts "configuration from #{path}" if ConfigLoader.debug?
11
13
  config = ConfigLoader.merge_with_default(config, path)
12
14
  ConfigLoader.instance_variable_set(:@default_configuration, config)