Skip to content

Commit

Permalink
Merge pull request #1373 from r7kamura/feature/array-new
Browse files Browse the repository at this point in the history
Support `Array.new(n)` on `RSpec/FactoryBot/CreateList` cop
  • Loading branch information
Darhazer committed Sep 12, 2022
2 parents a5cbcf2 + 4ba3063 commit 71a3efb
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* Add `RSpec/ClassCheck` cop. ([@r7kamura][])
* Fix a false positive for `RSpec/Capybara/SpecificMatcher` when pseudo-classes. ([@ydah][])
* Fix a false negative for `RSpec/SubjectStub` when the subject is declared with the `subject!` method and called by name. ([@eikes][])
* Support `Array.new(n)` on `RSpec/FactoryBot/CreateList` cop. ([@r7kamura][])

## 2.12.1 (2022-07-03)

Expand Down
35 changes: 24 additions & 11 deletions lib/rubocop/cop/rspec/factory_bot/create_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,23 @@ class CreateList < Base
MSG_N_TIMES = 'Prefer %<number>s.times.'
RESTRICT_ON_SEND = %i[create_list].freeze

# @!method n_times_block?(node)
def_node_matcher :n_times_block?, <<-PATTERN
# @!method array_new_or_n_times_block?(node)
def_node_matcher :array_new_or_n_times_block?, <<-PATTERN
(block
(send (int _) :times)
{
(send (const {nil? | cbase} :Array) :new (int _)) |
(send (int _) :times)
}
...
)
PATTERN

# @!method n_times_block_with_arg_and_used?(node)
def_node_matcher :n_times_block_with_arg_and_used?, <<-PATTERN
# @!method block_with_arg_and_used?(node)
def_node_matcher :block_with_arg_and_used?, <<-PATTERN
(block
(send (int _) :times)
_
(args (arg _value))
`_value
`_value
)
PATTERN

Expand All @@ -75,8 +78,8 @@ class CreateList < Base
def on_block(node) # rubocop:todo InternalAffairs/NumblockHandler
return unless style == :create_list

return unless n_times_block?(node)
return if n_times_block_with_arg_and_used?(node)
return unless array_new_or_n_times_block?(node)
return if block_with_arg_and_used?(node)
return unless node.body
return if arguments_include_method_call?(node.body)
return unless contains_only_factory?(node.body)
Expand Down Expand Up @@ -184,7 +187,7 @@ def call(corrector)

def call_with_block_replacement(node)
block = node.body
arguments = build_arguments(block, node.receiver.source)
arguments = build_arguments(block, count_from(node))
replacement = format_receiver(block.receiver)
replacement += format_method_call(block, 'create_list', arguments)
replacement += format_block(block)
Expand All @@ -204,7 +207,7 @@ def call_replacement(node)
block = node.body
factory, *options = *block.arguments

arguments = "#{factory.source}, #{node.receiver.source}"
arguments = "#{factory.source}, #{count_from(node)}"
options = build_options_string(options)
arguments += ", #{options}" unless options.empty?

Expand All @@ -213,6 +216,16 @@ def call_replacement(node)
replacement
end

def count_from(node)
count_node =
if node.receiver.int_type?
node.receiver
else
node.send_node.first_argument
end
count_node.source
end

def format_block(node)
if node.body.begin_type?
format_multiline_block(node)
Expand Down
24 changes: 24 additions & 0 deletions spec/rubocop/cop/rspec/factory_bot/create_list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,30 @@
end
RUBY
end

it 'flags usage of Array.new(n) with no arguments' do
expect_offense(<<~RUBY)
Array.new(3) { create(:user) }
^^^^^^^^^^^^ Prefer create_list.
RUBY

expect_correction(<<~RUBY)
create_list(:user, 3)
RUBY
end

it 'flags usage of Array.new(n) with block argument' do
expect_offense(<<~RUBY)
Array.new(3) do
^^^^^^^^^^^^ Prefer create_list.
create(:user) { |user| create(:account, user: user) }
end
RUBY

expect_correction(<<~RUBY)
create_list(:user, 3) { |user| create(:account, user: user) }
RUBY
end
end

context 'when EnforcedStyle is :n_times' do
Expand Down

0 comments on commit 71a3efb

Please sign in to comment.