rubocop-rspec 1.7.0 → 3.0.2

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 (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)