rubocop-rspec 2.20.0 → 2.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ebfcc955ecfa8b3437defde668d955020f03a2dbba2c9337f0b75a8fb9a3c26
4
- data.tar.gz: 7a547b1e24c278de072b2c912be27e1dc5fb1450b6fa65d563278d0f054d6d1d
3
+ metadata.gz: e786386e218833ac7f78af9ffcadadb6bf6c822a31b455cac1567152fd3bc9c0
4
+ data.tar.gz: 384170b4ae3a06c05b30855ae9361057c53226b40b924d4ec1d49c0d15195658
5
5
  SHA512:
6
- metadata.gz: f8942cdf262718de32bfbc765e3f2aac9eae49e364d7a2d866300b77e0bd39fd24b168d513577a1a95a82ce10a0f325a66c6b2914da71dd3951011c2399b0b3e
7
- data.tar.gz: 89c8fce01b42c53c50460b9411cd44f96b99663ce3a2e7735e6cb0f260c1cc3a86ba0daba553b4ebeba25625dde25f41d8b2129929cf92514e06e0f0f8191ec5
6
+ metadata.gz: 554345811f404b27a39fd522e0085022c39147e0487f04ea77ed7887002451af112e2ddc45ad94841c0a4201a76f37fbfb7a2e64aed3c7efa91cddf1a30843bc
7
+ data.tar.gz: 22882a56ddf03c55d6eaf299fb05197c835049054231e6d7798e2ca3eac5cbd9840eeada823a12431b00efd37289a8353792dfe3c8f653b2606f9ccda480615a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Master (Unreleased)
4
4
 
5
+ ## 2.22.0 (2023-05-06)
6
+
7
+ - Extract factory_bot cops to a separate repository, [`rubocop-factory_bot`](https://github.com/rubocop/rubocop-factory_bot). The `rubocop-factory_bot` repository is a dependency of `rubocop-rspec` and the factory_bot cops are aliased (`RSpec/FactoryBot/Foo` == `FactoryBot/Foo`) until v3.0 is released, so the change will be invisible to users until then. ([@ydah])
8
+
9
+ ## 2.21.0 (2023-05-05)
10
+
11
+ - Fix a false positive in `RSpec/IndexedLet` with suffixes after index-like numbers. ([@pirj])
12
+ - Fix an error for `RSpec/Rails/HaveHttpStatus` with comparison with strings containing non-numeric characters. ([@ydah])
13
+ - Fix an error for `RSpec/MatchArray` when `match_array` with no argument. ([@ydah])
14
+ - Add support `a_block_changing` and `changing` for `RSpec/ChangeByZero`. ([@ydah])
15
+ - Drop Ruby 2.6 support. ([@ydah])
16
+
5
17
  ## 2.20.0 (2023-04-18)
6
18
 
7
19
  - Add new `RSpec/IndexedLet` cop. ([@dmitrytsepelev])
@@ -21,3 +21,9 @@ renamed:
21
21
  RSpec/Capybara/SpecificFinders: Capybara/SpecificFinders
22
22
  RSpec/Capybara/SpecificMatcher: Capybara/SpecificMatcher
23
23
  RSpec/Capybara/VisibilityMatcher: Capybara/VisibilityMatcher
24
+ RSpec/FactoryBot/AttributeDefinedStatically: FactoryBot/AttributeDefinedStatically
25
+ RSpec/FactoryBot/ConsistentParenthesesStyle: FactoryBot/ConsistentParenthesesStyle
26
+ RSpec/FactoryBot/CreateList: FactoryBot/CreateList
27
+ RSpec/FactoryBot/FactoryClassName: FactoryBot/FactoryClassName
28
+ RSpec/FactoryBot/FactoryNameStyle: FactoryBot/FactoryNameStyle
29
+ RSpec/FactoryBot/SyntaxMethods: FactoryBot/SyntaxMethods
@@ -59,15 +59,16 @@ module RuboCop
59
59
  #
60
60
  class ChangeByZero < Base
61
61
  extend AutoCorrector
62
- MSG = 'Prefer `not_to change` over `to change.by(0)`.'
62
+ MSG = 'Prefer `not_to change` over `to %<method>s.by(0)`.'
63
63
  MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
64
- 'over `change.by(0)`.'
65
- RESTRICT_ON_SEND = %i[change].freeze
64
+ 'over `%<method>s.by(0)`.'
65
+ CHANGE_METHODS = Set[:change, :a_block_changing, :changing].freeze
66
+ RESTRICT_ON_SEND = CHANGE_METHODS.freeze
66
67
 
67
68
  # @!method expect_change_with_arguments(node)
68
69
  def_node_matcher :expect_change_with_arguments, <<-PATTERN
69
70
  (send
70
- (send nil? :change ...) :by
71
+ $(send nil? CHANGE_METHODS ...) :by
71
72
  (int 0))
72
73
  PATTERN
73
74
 
@@ -75,48 +76,61 @@ module RuboCop
75
76
  def_node_matcher :expect_change_with_block, <<-PATTERN
76
77
  (send
77
78
  (block
78
- (send nil? :change)
79
+ $(send nil? CHANGE_METHODS)
79
80
  (args)
80
- (send (...) $_)) :by
81
+ (send (...) _)) :by
81
82
  (int 0))
82
83
  PATTERN
83
84
 
84
85
  # @!method change_nodes(node)
85
86
  def_node_search :change_nodes, <<-PATTERN
86
- $(send nil? :change ...)
87
+ $(send nil? CHANGE_METHODS ...)
87
88
  PATTERN
88
89
 
89
90
  def on_send(node)
90
- expect_change_with_arguments(node.parent) do
91
- check_offense(node.parent)
91
+ expect_change_with_arguments(node.parent) do |change|
92
+ register_offense(node.parent, change)
92
93
  end
93
94
 
94
- expect_change_with_block(node.parent.parent) do
95
- check_offense(node.parent.parent)
95
+ expect_change_with_block(node.parent.parent) do |change|
96
+ register_offense(node.parent.parent, change)
96
97
  end
97
98
  end
98
99
 
99
100
  private
100
101
 
101
- def check_offense(node)
102
- expression = node.source_range
102
+ # rubocop:disable Metrics/MethodLength
103
+ def register_offense(node, change_node)
103
104
  if compound_expectations?(node)
104
- add_offense(expression, message: message_compound) do |corrector|
105
+ add_offense(node.source_range,
106
+ message: message_compound(change_node)) do |corrector|
105
107
  autocorrect_compound(corrector, node)
106
108
  end
107
109
  else
108
- add_offense(expression) do |corrector|
109
- autocorrect(corrector, node)
110
+ add_offense(node.source_range,
111
+ message: message(change_node)) do |corrector|
112
+ autocorrect(corrector, node, change_node)
110
113
  end
111
114
  end
112
115
  end
116
+ # rubocop:enable Metrics/MethodLength
113
117
 
114
118
  def compound_expectations?(node)
115
119
  %i[and or & |].include?(node.parent.method_name)
116
120
  end
117
121
 
118
- def autocorrect(corrector, node)
122
+ def message(change_node)
123
+ format(MSG, method: change_node.method_name)
124
+ end
125
+
126
+ def message_compound(change_node)
127
+ format(MSG_COMPOUND, preferred: preferred_method,
128
+ method: change_node.method_name)
129
+ end
130
+
131
+ def autocorrect(corrector, node, change_node)
119
132
  corrector.replace(node.parent.loc.selector, 'not_to')
133
+ corrector.replace(change_node.loc.selector, 'change')
120
134
  range = node.loc.dot.with(end_pos: node.source_range.end_pos)
121
135
  corrector.remove(range)
122
136
  end
@@ -135,10 +149,6 @@ module RuboCop
135
149
  cop_config['NegatedMatcher']
136
150
  end
137
151
 
138
- def message_compound
139
- format(MSG_COMPOUND, preferred: preferred_method)
140
- end
141
-
142
152
  def preferred_method
143
153
  negated_matcher ? "`#{negated_matcher}`" : 'negated matchers'
144
154
  end
@@ -4,124 +4,31 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Always declare attribute values as blocks.
8
- #
9
- # @example
10
- # # bad
11
- # kind [:active, :rejected].sample
12
- #
13
- # # good
14
- # kind { [:active, :rejected].sample }
15
- #
16
- # # bad
17
- # closed_at 1.day.from_now
18
- #
19
- # # good
20
- # closed_at { 1.day.from_now }
21
- #
22
- # # bad
23
- # count 1
24
- #
25
- # # good
26
- # count { 1 }
27
- #
28
- class AttributeDefinedStatically < ::RuboCop::Cop::Base
29
- extend AutoCorrector
30
-
31
- MSG = 'Use a block to declare attribute values.'
32
-
33
- # @!method value_matcher(node)
34
- def_node_matcher :value_matcher, <<-PATTERN
35
- (send _ !#reserved_method? $...)
36
- PATTERN
37
-
38
- # @!method factory_attributes(node)
39
- def_node_matcher :factory_attributes, <<-PATTERN
40
- (block (send _ #attribute_defining_method? ...) _ { (begin $...) $(send ...) } )
41
- PATTERN
42
-
43
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
44
- attributes = factory_attributes(node) || []
45
- attributes = [attributes] unless attributes.is_a?(Array) # rubocop:disable Style/ArrayCoercion, Lint/RedundantCopDisableDirective
46
-
47
- attributes.each do |attribute|
48
- next unless offensive_receiver?(attribute.receiver, node)
49
- next if proc?(attribute) || association?(attribute.first_argument)
50
-
51
- add_offense(attribute) do |corrector|
52
- autocorrect(corrector, attribute)
53
- end
54
- end
55
- end
56
-
57
- private
58
-
59
- def autocorrect(corrector, node)
60
- if node.parenthesized?
61
- autocorrect_replacing_parens(corrector, node)
62
- else
63
- autocorrect_without_parens(corrector, node)
64
- end
65
- end
66
-
67
- def offensive_receiver?(receiver, node)
68
- receiver.nil? ||
69
- receiver.self_type? ||
70
- receiver_matches_first_block_argument?(receiver, node)
71
- end
72
-
73
- def receiver_matches_first_block_argument?(receiver, node)
74
- first_block_argument = node.arguments.first
75
-
76
- !first_block_argument.nil? &&
77
- receiver.lvar_type? &&
78
- receiver.node_parts == first_block_argument.node_parts
79
- end
80
-
81
- def proc?(attribute)
82
- value_matcher(attribute).to_a.all?(&:block_pass_type?)
83
- end
84
-
85
- # @!method association?(node)
86
- def_node_matcher :association?, '(hash <(pair (sym :factory) _) ...>)'
87
-
88
- def autocorrect_replacing_parens(corrector, node)
89
- left_braces, right_braces = braces(node)
90
-
91
- corrector.replace(node.location.begin, " #{left_braces}")
92
- corrector.replace(node.location.end, right_braces)
93
- end
94
-
95
- def autocorrect_without_parens(corrector, node)
96
- left_braces, right_braces = braces(node)
97
-
98
- argument = node.first_argument
99
- expression = argument.source_range
100
- corrector.insert_before(expression, left_braces)
101
- corrector.insert_after(expression, right_braces)
102
- end
103
-
104
- def braces(node)
105
- if value_hash_without_braces?(node.first_argument)
106
- ['{ { ', ' } }']
107
- else
108
- ['{ ', ' }']
109
- end
110
- end
111
-
112
- def value_hash_without_braces?(node)
113
- node.hash_type? && !node.braces?
114
- end
115
-
116
- def reserved_method?(method_name)
117
- RuboCop::RSpec::FactoryBot.reserved_methods.include?(method_name)
118
- end
119
-
120
- def attribute_defining_method?(method_name)
121
- RuboCop::RSpec::FactoryBot.attribute_defining_methods
122
- .include?(method_name)
123
- end
124
- end
7
+ # @!parse
8
+ # # Always declare attribute values as blocks.
9
+ # #
10
+ # # @example
11
+ # # # bad
12
+ # # kind [:active, :rejected].sample
13
+ # #
14
+ # # # good
15
+ # # kind { [:active, :rejected].sample }
16
+ # #
17
+ # # # bad
18
+ # # closed_at 1.day.from_now
19
+ # #
20
+ # # # good
21
+ # # closed_at { 1.day.from_now }
22
+ # #
23
+ # # # bad
24
+ # # count 1
25
+ # #
26
+ # # # good
27
+ # # count { 1 }
28
+ # #
29
+ # class AttributeDefinedStatically < ::RuboCop::Cop::Base; end
30
+ AttributeDefinedStatically =
31
+ ::RuboCop::Cop::FactoryBot::AttributeDefinedStatically
125
32
  end
126
33
  end
127
34
  end
@@ -4,113 +4,46 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Use a consistent style for parentheses in factory bot calls.
8
- #
9
- # @example
10
- #
11
- # # bad
12
- # create :user
13
- # build(:user)
14
- # create(:login)
15
- # create :login
16
- #
17
- # @example `EnforcedStyle: require_parentheses` (default)
18
- #
19
- # # good
20
- # create(:user)
21
- # create(:user)
22
- # create(:login)
23
- # build(:login)
24
- #
25
- # @example `EnforcedStyle: omit_parentheses`
26
- #
27
- # # good
28
- # create :user
29
- # build :user
30
- # create :login
31
- # create :login
32
- #
33
- # # also good
34
- # # when method name and first argument are not on same line
35
- # create(
36
- # :user
37
- # )
38
- # build(
39
- # :user,
40
- # name: 'foo'
41
- # )
42
- #
43
- class ConsistentParenthesesStyle < ::RuboCop::Cop::Base
44
- extend AutoCorrector
45
- include ConfigurableEnforcedStyle
46
- include RuboCop::RSpec::FactoryBot::Language
47
- include RuboCop::Cop::Util
48
-
49
- def self.autocorrect_incompatible_with
50
- [Style::MethodCallWithArgsParentheses]
51
- end
52
-
53
- MSG_REQUIRE_PARENS = 'Prefer method call with parentheses'
54
- MSG_OMIT_PARENS = 'Prefer method call without parentheses'
55
-
56
- FACTORY_CALLS = RuboCop::RSpec::FactoryBot::Language::METHODS
57
-
58
- RESTRICT_ON_SEND = FACTORY_CALLS
59
-
60
- # @!method factory_call(node)
61
- def_node_matcher :factory_call, <<-PATTERN
62
- (send
63
- {#factory_bot? nil?} %FACTORY_CALLS
64
- {sym str send lvar} _*
65
- )
66
- PATTERN
67
-
68
- def on_send(node)
69
- return if ambiguous_without_parentheses?(node)
70
-
71
- factory_call(node) do
72
- return if node.method?(:generate) && node.arguments.count > 1
73
-
74
- if node.parenthesized?
75
- process_with_parentheses(node)
76
- else
77
- process_without_parentheses(node)
78
- end
79
- end
80
- end
81
-
82
- private
83
-
84
- def process_with_parentheses(node)
85
- return unless style == :omit_parentheses
86
- return unless same_line?(node, node.first_argument)
87
-
88
- add_offense(node.loc.selector,
89
- message: MSG_OMIT_PARENS) do |corrector|
90
- remove_parentheses(corrector, node)
91
- end
92
- end
93
-
94
- def process_without_parentheses(node)
95
- return unless style == :require_parentheses
96
-
97
- add_offense(node.loc.selector,
98
- message: MSG_REQUIRE_PARENS) do |corrector|
99
- add_parentheses(node, corrector)
100
- end
101
- end
102
-
103
- AMBIGUOUS_TYPES = %i[send pair array and or if].freeze
104
-
105
- def ambiguous_without_parentheses?(node)
106
- node.parent && AMBIGUOUS_TYPES.include?(node.parent.type)
107
- end
108
-
109
- def remove_parentheses(corrector, node)
110
- corrector.replace(node.location.begin, ' ')
111
- corrector.remove(node.location.end)
112
- end
113
- end
7
+ # @!parse
8
+ # # Use a consistent style for parentheses in factory bot calls.
9
+ # #
10
+ # # @example
11
+ # #
12
+ # # # bad
13
+ # # create :user
14
+ # # build(:user)
15
+ # # create(:login)
16
+ # # create :login
17
+ # #
18
+ # # @example `EnforcedStyle: require_parentheses` (default)
19
+ # #
20
+ # # # good
21
+ # # create(:user)
22
+ # # create(:user)
23
+ # # create(:login)
24
+ # # build(:login)
25
+ # #
26
+ # # @example `EnforcedStyle: omit_parentheses`
27
+ # #
28
+ # # # good
29
+ # # create :user
30
+ # # build :user
31
+ # # create :login
32
+ # # create :login
33
+ # #
34
+ # # # also good
35
+ # # # when method name and first argument are not on same line
36
+ # # create(
37
+ # # :user
38
+ # # )
39
+ # # build(
40
+ # # :user,
41
+ # # name: 'foo'
42
+ # # )
43
+ # #
44
+ # class ConsistentParenthesesStyle < ::RuboCop::Cop::Base; end
45
+ ConsistentParenthesesStyle =
46
+ ::RuboCop::Cop::FactoryBot::ConsistentParenthesesStyle
114
47
  end
115
48
  end
116
49
  end
@@ -4,256 +4,36 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Checks for create_list usage.
8
- #
9
- # This cop can be configured using the `EnforcedStyle` option
10
- #
11
- # @example `EnforcedStyle: create_list` (default)
12
- # # bad
13
- # 3.times { create :user }
14
- #
15
- # # good
16
- # create_list :user, 3
17
- #
18
- # # bad
19
- # 3.times { create :user, age: 18 }
20
- #
21
- # # good - index is used to alter the created models attributes
22
- # 3.times { |n| create :user, age: n }
23
- #
24
- # # good - contains a method call, may return different values
25
- # 3.times { create :user, age: rand }
26
- #
27
- # @example `EnforcedStyle: n_times`
28
- # # bad
29
- # create_list :user, 3
30
- #
31
- # # good
32
- # 3.times { create :user }
33
- #
34
- class CreateList < ::RuboCop::Cop::Base
35
- extend AutoCorrector
36
- include ConfigurableEnforcedStyle
37
- include RuboCop::RSpec::FactoryBot::Language
38
-
39
- MSG_CREATE_LIST = 'Prefer create_list.'
40
- MSG_N_TIMES = 'Prefer %<number>s.times.'
41
- RESTRICT_ON_SEND = %i[create_list].freeze
42
-
43
- # @!method array_new_or_n_times_block?(node)
44
- def_node_matcher :array_new_or_n_times_block?, <<-PATTERN
45
- (block
46
- {
47
- (send (const {nil? | cbase} :Array) :new (int _)) |
48
- (send (int _) :times)
49
- }
50
- ...
51
- )
52
- PATTERN
53
-
54
- # @!method block_with_arg_and_used?(node)
55
- def_node_matcher :block_with_arg_and_used?, <<-PATTERN
56
- (block
57
- _
58
- (args (arg _value))
59
- `_value
60
- )
61
- PATTERN
62
-
63
- # @!method arguments_include_method_call?(node)
64
- def_node_matcher :arguments_include_method_call?, <<-PATTERN
65
- (send ${nil? #factory_bot?} :create (sym $_) `$(send ...))
66
- PATTERN
67
-
68
- # @!method factory_call(node)
69
- def_node_matcher :factory_call, <<-PATTERN
70
- (send ${nil? #factory_bot?} :create (sym $_) $...)
71
- PATTERN
72
-
73
- # @!method factory_list_call(node)
74
- def_node_matcher :factory_list_call, <<-PATTERN
75
- (send {nil? #factory_bot?} :create_list (sym _) (int $_) ...)
76
- PATTERN
77
-
78
- def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
79
- return unless style == :create_list
80
-
81
- return unless array_new_or_n_times_block?(node)
82
- return if block_with_arg_and_used?(node)
83
- return unless node.body
84
- return if arguments_include_method_call?(node.body)
85
- return unless contains_only_factory?(node.body)
86
-
87
- add_offense(node.send_node, message: MSG_CREATE_LIST) do |corrector|
88
- CreateListCorrector.new(node.send_node).call(corrector)
89
- end
90
- end
91
-
92
- def on_send(node)
93
- return unless style == :n_times
94
-
95
- factory_list_call(node) do |count|
96
- message = format(MSG_N_TIMES, number: count)
97
- add_offense(node.loc.selector, message: message) do |corrector|
98
- TimesCorrector.new(node).call(corrector)
99
- end
100
- end
101
- end
102
-
103
- private
104
-
105
- def contains_only_factory?(node)
106
- if node.block_type?
107
- factory_call(node.send_node)
108
- else
109
- factory_call(node)
110
- end
111
- end
112
-
113
- # :nodoc
114
- module Corrector
115
- private
116
-
117
- def build_options_string(options)
118
- options.map(&:source).join(', ')
119
- end
120
-
121
- def format_method_call(node, method, arguments)
122
- if node.block_type? || node.parenthesized?
123
- "#{method}(#{arguments})"
124
- else
125
- "#{method} #{arguments}"
126
- end
127
- end
128
-
129
- def format_receiver(receiver)
130
- return '' unless receiver
131
-
132
- "#{receiver.source}."
133
- end
134
- end
135
-
136
- # :nodoc
137
- class TimesCorrector
138
- include Corrector
139
-
140
- def initialize(node)
141
- @node = node
142
- end
143
-
144
- def call(corrector)
145
- replacement = generate_n_times_block(node)
146
- corrector.replace(node.block_node || node, replacement)
147
- end
148
-
149
- private
150
-
151
- attr_reader :node
152
-
153
- def generate_n_times_block(node)
154
- factory, count, *options = node.arguments
155
-
156
- arguments = factory.source
157
- options = build_options_string(options)
158
- arguments += ", #{options}" unless options.empty?
159
-
160
- replacement = format_receiver(node.receiver)
161
- replacement += format_method_call(node, 'create', arguments)
162
- replacement += " #{factory_call_block_source}" if node.block_node
163
- "#{count.source}.times { #{replacement} }"
164
- end
165
-
166
- def factory_call_block_source
167
- node.block_node.location.begin.with(
168
- end_pos: node.block_node.location.end.end_pos
169
- ).source
170
- end
171
- end
172
-
173
- # :nodoc:
174
- class CreateListCorrector
175
- include Corrector
176
-
177
- def initialize(node)
178
- @node = node.parent
179
- end
180
-
181
- def call(corrector)
182
- replacement = if node.body.block_type?
183
- call_with_block_replacement(node)
184
- else
185
- call_replacement(node)
186
- end
187
-
188
- corrector.replace(node, replacement)
189
- end
190
-
191
- private
192
-
193
- attr_reader :node
194
-
195
- def call_with_block_replacement(node)
196
- block = node.body
197
- arguments = build_arguments(block, count_from(node))
198
- replacement = format_receiver(block.receiver)
199
- replacement += format_method_call(block, 'create_list', arguments)
200
- replacement += format_block(block)
201
- replacement
202
- end
203
-
204
- def build_arguments(node, count)
205
- factory, *options = *node.send_node.arguments
206
-
207
- arguments = ":#{factory.value}, #{count}"
208
- options = build_options_string(options)
209
- arguments += ", #{options}" unless options.empty?
210
- arguments
211
- end
212
-
213
- def call_replacement(node)
214
- block = node.body
215
- factory, *options = *block.arguments
216
-
217
- arguments = "#{factory.source}, #{count_from(node)}"
218
- options = build_options_string(options)
219
- arguments += ", #{options}" unless options.empty?
220
-
221
- replacement = format_receiver(block.receiver)
222
- replacement += format_method_call(block, 'create_list', arguments)
223
- replacement
224
- end
225
-
226
- def count_from(node)
227
- count_node =
228
- if node.receiver.int_type?
229
- node.receiver
230
- else
231
- node.send_node.first_argument
232
- end
233
- count_node.source
234
- end
235
-
236
- def format_block(node)
237
- if node.body.begin_type?
238
- format_multiline_block(node)
239
- else
240
- format_singleline_block(node)
241
- end
242
- end
243
-
244
- def format_multiline_block(node)
245
- indent = ' ' * node.body.loc.column
246
- indent_end = ' ' * node.parent.loc.column
247
- " do #{node.arguments.source}\n" \
248
- "#{indent}#{node.body.source}\n" \
249
- "#{indent_end}end"
250
- end
251
-
252
- def format_singleline_block(node)
253
- " { #{node.arguments.source} #{node.body.source} }"
254
- end
255
- end
256
- end
7
+ # @!parse
8
+ # # Checks for create_list usage.
9
+ # #
10
+ # # This cop can be configured using the `EnforcedStyle` option
11
+ # #
12
+ # # @example `EnforcedStyle: create_list` (default)
13
+ # # # bad
14
+ # # 3.times { create :user }
15
+ # #
16
+ # # # good
17
+ # # create_list :user, 3
18
+ # #
19
+ # # # bad
20
+ # # 3.times { create :user, age: 18 }
21
+ # #
22
+ # # # good - index is used to alter the created models attributes
23
+ # # 3.times { |n| create :user, age: n }
24
+ # #
25
+ # # # good - contains a method call, may return different values
26
+ # # 3.times { create :user, age: rand }
27
+ # #
28
+ # # @example `EnforcedStyle: n_times`
29
+ # # # bad
30
+ # # create_list :user, 3
31
+ # #
32
+ # # # good
33
+ # # 3.times { create :user }
34
+ # #
35
+ # class CreateList < ::RuboCop::Cop::Base; end
36
+ CreateList = ::RuboCop::Cop::FactoryBot::CreateList
257
37
  end
258
38
  end
259
39
  end
@@ -4,52 +4,25 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Use string value when setting the class attribute explicitly.
8
- #
9
- # This cop would promote faster tests by lazy-loading of
10
- # application files. Also, this could help you suppress potential bugs
11
- # in combination with external libraries by avoiding a preload of
12
- # application files from the factory files.
13
- #
14
- # @example
15
- # # bad
16
- # factory :foo, class: Foo do
17
- # end
18
- #
19
- # # good
20
- # factory :foo, class: 'Foo' do
21
- # end
22
- #
23
- class FactoryClassName < ::RuboCop::Cop::Base
24
- extend AutoCorrector
25
-
26
- MSG = "Pass '%<class_name>s' string instead of `%<class_name>s` " \
27
- 'constant.'
28
- ALLOWED_CONSTANTS = %w[Hash OpenStruct].freeze
29
- RESTRICT_ON_SEND = %i[factory].freeze
30
-
31
- # @!method class_name(node)
32
- def_node_matcher :class_name, <<~PATTERN
33
- (send _ :factory _ (hash <(pair (sym :class) $(const ...)) ...>))
34
- PATTERN
35
-
36
- def on_send(node)
37
- class_name(node) do |cn|
38
- next if allowed?(cn.const_name)
39
-
40
- msg = format(MSG, class_name: cn.const_name)
41
- add_offense(cn, message: msg) do |corrector|
42
- corrector.replace(cn, "'#{cn.source}'")
43
- end
44
- end
45
- end
46
-
47
- private
48
-
49
- def allowed?(const_name)
50
- ALLOWED_CONSTANTS.include?(const_name)
51
- end
52
- end
7
+ # @!parse
8
+ # # Use string value when setting the class attribute explicitly.
9
+ # #
10
+ # # This cop would promote faster tests by lazy-loading of
11
+ # # application files. Also, this could help you suppress potential
12
+ # # bugs in combination with external libraries by avoiding a preload
13
+ # # of application files from the factory files.
14
+ # #
15
+ # # @example
16
+ # # # bad
17
+ # # factory :foo, class: Foo do
18
+ # # end
19
+ # #
20
+ # # # good
21
+ # # factory :foo, class: 'Foo' do
22
+ # # end
23
+ # #
24
+ # class FactoryClassName < ::RuboCop::Cop::Base; end
25
+ FactoryClassName = ::RuboCop::Cop::FactoryBot::FactoryClassName
53
26
  end
54
27
  end
55
28
  end
@@ -4,70 +4,29 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Checks for name style for argument of FactoryBot::Syntax::Methods.
8
- #
9
- # @example EnforcedStyle: symbol (default)
10
- # # bad
11
- # create('user')
12
- # build "user", username: "NAME"
13
- #
14
- # # good
15
- # create(:user)
16
- # build :user, username: "NAME"
17
- #
18
- # @example EnforcedStyle: string
19
- # # bad
20
- # create(:user)
21
- # build :user, username: "NAME"
22
- #
23
- # # good
24
- # create('user')
25
- # build "user", username: "NAME"
26
- #
27
- class FactoryNameStyle < ::RuboCop::Cop::Base
28
- extend AutoCorrector
29
- include ConfigurableEnforcedStyle
30
- include RuboCop::RSpec::FactoryBot::Language
31
-
32
- MSG = 'Use %<prefer>s to refer to a factory.'
33
- FACTORY_CALLS = RuboCop::RSpec::FactoryBot::Language::METHODS
34
- RESTRICT_ON_SEND = FACTORY_CALLS
35
-
36
- # @!method factory_call(node)
37
- def_node_matcher :factory_call, <<-PATTERN
38
- (send
39
- {#factory_bot? nil?} %FACTORY_CALLS
40
- ${str sym} ...
41
- )
42
- PATTERN
43
-
44
- def on_send(node)
45
- factory_call(node) do |name|
46
- if offense_for_symbol_style?(name)
47
- register_offense(name, name.value.to_sym.inspect)
48
- elsif offense_for_string_style?(name)
49
- register_offense(name, name.value.to_s.inspect)
50
- end
51
- end
52
- end
53
-
54
- private
55
-
56
- def offense_for_symbol_style?(name)
57
- name.str_type? && style == :symbol
58
- end
59
-
60
- def offense_for_string_style?(name)
61
- name.sym_type? && style == :string
62
- end
63
-
64
- def register_offense(name, prefer)
65
- add_offense(name,
66
- message: format(MSG, prefer: style.to_s)) do |corrector|
67
- corrector.replace(name, prefer)
68
- end
69
- end
70
- end
7
+ # @!parse
8
+ # # Checks for name style for argument of FactoryBot::Syntax::Methods.
9
+ # #
10
+ # # @example EnforcedStyle: symbol (default)
11
+ # # # bad
12
+ # # create('user')
13
+ # # build "user", username: "NAME"
14
+ # #
15
+ # # # good
16
+ # # create(:user)
17
+ # # build :user, username: "NAME"
18
+ # #
19
+ # # @example EnforcedStyle: string
20
+ # # # bad
21
+ # # create(:user)
22
+ # # build :user, username: "NAME"
23
+ # #
24
+ # # # good
25
+ # # create('user')
26
+ # # build "user", username: "NAME"
27
+ # #
28
+ # class FactoryNameStyle < ::RuboCop::Cop::Base; end
29
+ FactoryNameStyle = ::RuboCop::Cop::FactoryBot::FactoryNameStyle
71
30
  end
72
31
  end
73
32
  end
@@ -4,85 +4,51 @@ module RuboCop
4
4
  module Cop
5
5
  module RSpec
6
6
  module FactoryBot
7
- # Use shorthands from `FactoryBot::Syntax::Methods` in your specs.
8
- #
9
- # @safety
10
- # The autocorrection is marked as unsafe because the cop
11
- # cannot verify whether you already include
12
- # `FactoryBot::Syntax::Methods` in your test suite.
13
- #
14
- # If you're using Rails, add the following configuration to
15
- # `spec/support/factory_bot.rb` and be sure to require that file in
16
- # `rails_helper.rb`:
17
- #
18
- # [source,ruby]
19
- # ----
20
- # RSpec.configure do |config|
21
- # config.include FactoryBot::Syntax::Methods
22
- # end
23
- # ----
24
- #
25
- # If you're not using Rails:
26
- #
27
- # [source,ruby]
28
- # ----
29
- # RSpec.configure do |config|
30
- # config.include FactoryBot::Syntax::Methods
31
- #
32
- # config.before(:suite) do
33
- # FactoryBot.find_definitions
34
- # end
35
- # end
36
- # ----
37
- #
38
- # @example
39
- # # bad
40
- # FactoryBot.create(:bar)
41
- # FactoryBot.build(:bar)
42
- # FactoryBot.attributes_for(:bar)
43
- #
44
- # # good
45
- # create(:bar)
46
- # build(:bar)
47
- # attributes_for(:bar)
48
- #
49
- class SyntaxMethods < Base
50
- extend AutoCorrector
51
- include InsideExampleGroup
52
- include RangeHelp
53
- include RuboCop::RSpec::FactoryBot::Language
54
-
55
- MSG = 'Use `%<method>s` from `FactoryBot::Syntax::Methods`.'
56
-
57
- RESTRICT_ON_SEND = RuboCop::RSpec::FactoryBot::Language::METHODS
58
-
59
- def on_send(node)
60
- return unless factory_bot?(node.receiver)
61
- return unless inside_example_group?(node)
62
-
63
- message = format(MSG, method: node.method_name)
64
-
65
- add_offense(crime_scene(node), message: message) do |corrector|
66
- corrector.remove(offense(node))
67
- end
68
- end
69
-
70
- private
71
-
72
- def crime_scene(node)
73
- range_between(
74
- node.source_range.begin_pos,
75
- node.loc.selector.end_pos
76
- )
77
- end
78
-
79
- def offense(node)
80
- range_between(
81
- node.source_range.begin_pos,
82
- node.loc.selector.begin_pos
83
- )
84
- end
85
- end
7
+ # @!parse
8
+ # # Use shorthands from `FactoryBot::Syntax::Methods` in your specs.
9
+ # #
10
+ # # @safety
11
+ # # The autocorrection is marked as unsafe because the cop
12
+ # # cannot verify whether you already include
13
+ # # `FactoryBot::Syntax::Methods` in your test suite.
14
+ # #
15
+ # # If you're using Rails, add the following configuration to
16
+ # # `spec/support/factory_bot.rb` and be sure to require that file
17
+ # # in `rails_helper.rb`:
18
+ # #
19
+ # # [source,ruby]
20
+ # # ----
21
+ # # RSpec.configure do |config|
22
+ # # config.include FactoryBot::Syntax::Methods
23
+ # # end
24
+ # # ----
25
+ # #
26
+ # # If you're not using Rails:
27
+ # #
28
+ # # [source,ruby]
29
+ # # ----
30
+ # # RSpec.configure do |config|
31
+ # # config.include FactoryBot::Syntax::Methods
32
+ # #
33
+ # # config.before(:suite) do
34
+ # # FactoryBot.find_definitions
35
+ # # end
36
+ # # end
37
+ # # ----
38
+ # #
39
+ # # @example
40
+ # # # bad
41
+ # # FactoryBot.create(:bar)
42
+ # # FactoryBot.build(:bar)
43
+ # # FactoryBot.attributes_for(:bar)
44
+ # #
45
+ # # # good
46
+ # # create(:bar)
47
+ # # build(:bar)
48
+ # # attributes_for(:bar)
49
+ # #
50
+ # class SyntaxMethods < ::RuboCop::Cop::Base; end
51
+ SyntaxMethods = ::RuboCop::Cop::FactoryBot::SyntaxMethods
86
52
  end
87
53
  end
88
54
  end
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks that spec file paths are consistent and well-formed.
7
7
  #
8
8
  # By default, this checks that spec file paths are consistent with the
9
- # test subject and and enforces that it reflects the described
9
+ # test subject and enforces that it reflects the described
10
10
  # class/module and its optionally called out method.
11
11
  #
12
12
  # With the configuration option `IgnoreMethods` the called out method will
@@ -56,19 +56,24 @@ module RuboCop
56
56
 
57
57
  private
58
58
 
59
- INDEX_REGEX = /_?\d+/.freeze
59
+ SUFFIX_INDEX_REGEX = /_?\d+$/.freeze
60
+ INDEX_REGEX = /\d+/.freeze
60
61
 
61
62
  def filter_indexed_lets(candidates)
62
63
  candidates
63
64
  .filter { |node| indexed_let?(node) }
64
- .group_by { |node| let_name(node).to_s.gsub(INDEX_REGEX, '') }
65
+ .group_by { |node| let_name_stripped_index(node) }
65
66
  .values
66
67
  .filter { |lets| lets.length > cop_config['Max'] }
67
68
  .flatten
68
69
  end
69
70
 
70
71
  def indexed_let?(node)
71
- let?(node) && INDEX_REGEX.match?(let_name(node))
72
+ let?(node) && SUFFIX_INDEX_REGEX.match?(let_name(node))
73
+ end
74
+
75
+ def let_name_stripped_index(node)
76
+ let_name(node).to_s.gsub(INDEX_REGEX, '')
72
77
  end
73
78
  end
74
79
  end
@@ -34,7 +34,7 @@ module RuboCop
34
34
  PATTERN
35
35
 
36
36
  def on_send(node)
37
- return unless node.first_argument.array_type?
37
+ return unless node.first_argument&.array_type?
38
38
  return if match_array_with_empty_array?(node)
39
39
 
40
40
  check_populated_array(node)
@@ -18,7 +18,7 @@ module RuboCop
18
18
  extend AutoCorrector
19
19
 
20
20
  MSG =
21
- 'Prefer `expect(response).%<to>s have_http_status(%<status>i)` ' \
21
+ 'Prefer `expect(response).%<to>s have_http_status(%<status>s)` ' \
22
22
  'over `%<bad_code>s`.'
23
23
 
24
24
  RUNNERS = %i[to to_not not_to].to_set
@@ -37,12 +37,14 @@ module RuboCop
37
37
 
38
38
  def on_send(node)
39
39
  match_status(node) do |response_status, to, match, status|
40
+ return unless status.to_s.match?(/\A\d+\z/)
41
+
40
42
  message = format(MSG, to: to, status: status,
41
43
  bad_code: node.source)
42
44
  add_offense(node, message: message) do |corrector|
43
45
  corrector.replace(response_status, 'response')
44
46
  corrector.replace(match.loc.selector, 'have_http_status')
45
- corrector.replace(match.first_argument, status.to_i.to_s)
47
+ corrector.replace(match.first_argument, status.to_s)
46
48
  end
47
49
  end
48
50
  end
@@ -16,6 +16,12 @@ module RuboCop
16
16
  RSpec/Capybara/SpecificFinders
17
17
  RSpec/Capybara/SpecificMatcher
18
18
  RSpec/Capybara/VisibilityMatcher
19
+ RSpec/FactoryBot/AttributeDefinedStatically
20
+ RSpec/FactoryBot/ConsistentParenthesesStyle
21
+ RSpec/FactoryBot/CreateList
22
+ RSpec/FactoryBot/FactoryClassName
23
+ RSpec/FactoryBot/FactoryNameStyle
24
+ RSpec/FactoryBot/SyntaxMethods
19
25
  )
20
26
  AMENDMENTS = %(Metrics/BlockLength)
21
27
  COP_DOC_BASE_URL = 'https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/'
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module RSpec
5
5
  # Version information for the RSpec RuboCop plugin.
6
6
  module Version
7
- STRING = '2.20.0'
7
+ STRING = '2.22.0'
8
8
  end
9
9
  end
10
10
  end
data/lib/rubocop-rspec.rb CHANGED
@@ -5,6 +5,7 @@ require 'yaml'
5
5
 
6
6
  require 'rubocop'
7
7
  require 'rubocop-capybara'
8
+ require 'rubocop-factory_bot'
8
9
 
9
10
  require_relative 'rubocop/rspec'
10
11
  require_relative 'rubocop/rspec/inject'
@@ -16,8 +17,6 @@ require_relative 'rubocop/rspec/wording'
16
17
  # Dependent on `RuboCop::RSpec::Language::NodePattern`.
17
18
  require_relative 'rubocop/rspec/language'
18
19
 
19
- require_relative 'rubocop/rspec/factory_bot/language'
20
-
21
20
  require_relative 'rubocop/cop/rspec/mixin/final_end_location'
22
21
  require_relative 'rubocop/cop/rspec/mixin/inside_example_group'
23
22
  require_relative 'rubocop/cop/rspec/mixin/location_help'
@@ -37,7 +36,6 @@ require_relative 'rubocop/rspec/concept'
37
36
  require_relative 'rubocop/rspec/corrector/move_node'
38
37
  require_relative 'rubocop/rspec/example'
39
38
  require_relative 'rubocop/rspec/example_group'
40
- require_relative 'rubocop/rspec/factory_bot'
41
39
  require_relative 'rubocop/rspec/hook'
42
40
 
43
41
  RuboCop::RSpec::Inject.defaults!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.20.0
4
+ version: 2.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Backus
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-04-18 00:00:00.000000000 Z
13
+ date: 2023-05-06 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -40,6 +40,20 @@ dependencies:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
42
  version: '2.17'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rubocop-factory_bot
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.22'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '2.22'
43
57
  description: |2
44
58
  Code style checking for RSpec files.
45
59
  A plugin for the RuboCop code style enforcing & linting tool.
@@ -197,8 +211,6 @@ files:
197
211
  - lib/rubocop/rspec/description_extractor.rb
198
212
  - lib/rubocop/rspec/example.rb
199
213
  - lib/rubocop/rspec/example_group.rb
200
- - lib/rubocop/rspec/factory_bot.rb
201
- - lib/rubocop/rspec/factory_bot/language.rb
202
214
  - lib/rubocop/rspec/hook.rb
203
215
  - lib/rubocop/rspec/inject.rb
204
216
  - lib/rubocop/rspec/language.rb
@@ -222,14 +234,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
222
234
  requirements:
223
235
  - - ">="
224
236
  - !ruby/object:Gem::Version
225
- version: 2.6.0
237
+ version: 2.7.0
226
238
  required_rubygems_version: !ruby/object:Gem::Requirement
227
239
  requirements:
228
240
  - - ">="
229
241
  - !ruby/object:Gem::Version
230
242
  version: '0'
231
243
  requirements: []
232
- rubygems_version: 3.4.10
244
+ rubygems_version: 3.4.12
233
245
  signing_key:
234
246
  specification_version: 4
235
247
  summary: Code style checking for RSpec files
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module RSpec
5
- module FactoryBot
6
- # Contains node matchers for common FactoryBot DSL.
7
- module Language
8
- extend RuboCop::NodePattern::Macros
9
-
10
- METHODS = %i[
11
- attributes_for
12
- attributes_for_list
13
- attributes_for_pair
14
- build
15
- build_list
16
- build_pair
17
- build_stubbed
18
- build_stubbed_list
19
- build_stubbed_pair
20
- create
21
- create_list
22
- create_pair
23
- generate
24
- generate_list
25
- null
26
- null_list
27
- null_pair
28
- ].to_set.freeze
29
-
30
- # @!method factory_bot?(node)
31
- def_node_matcher :factory_bot?, <<~PATTERN
32
- (const {nil? cbase} {:FactoryGirl :FactoryBot})
33
- PATTERN
34
- end
35
- end
36
- end
37
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module RuboCop
4
- module RSpec
5
- # RuboCop FactoryBot project namespace
6
- module FactoryBot
7
- ATTRIBUTE_DEFINING_METHODS = %i[
8
- factory
9
- ignore
10
- trait
11
- traits_for_enum
12
- transient
13
- ].freeze
14
-
15
- UNPROXIED_METHODS = %i[
16
- __send__
17
- __id__
18
- nil?
19
- send
20
- object_id
21
- extend
22
- instance_eval
23
- initialize
24
- block_given?
25
- raise
26
- caller
27
- method
28
- ].freeze
29
-
30
- DEFINITION_PROXY_METHODS = %i[
31
- add_attribute
32
- after
33
- association
34
- before
35
- callback
36
- ignore
37
- initialize_with
38
- sequence
39
- skip_create
40
- to_create
41
- ].freeze
42
-
43
- RESERVED_METHODS =
44
- DEFINITION_PROXY_METHODS +
45
- UNPROXIED_METHODS +
46
- ATTRIBUTE_DEFINING_METHODS
47
-
48
- private_constant(
49
- :ATTRIBUTE_DEFINING_METHODS,
50
- :UNPROXIED_METHODS,
51
- :DEFINITION_PROXY_METHODS,
52
- :RESERVED_METHODS
53
- )
54
-
55
- def self.attribute_defining_methods
56
- ATTRIBUTE_DEFINING_METHODS
57
- end
58
-
59
- def self.reserved_methods
60
- RESERVED_METHODS
61
- end
62
- end
63
- end
64
- end