Skip to content

Commit

Permalink
Fix an incorrect autocorrect for RSpec/ChangeByZero when compound e…
Browse files Browse the repository at this point in the history
…xpectations with line break before `.by(0)`

Fix: #1815
  • Loading branch information
ydah committed Feb 26, 2024
1 parent b5f1e25 commit 91b1624
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -17,6 +17,7 @@
- Add configuration option `ResponseMethods` to `RSpec/Rails/HaveHttpStatus`. ([@ydah])
- Fix a false negative for `RSpec/DescribedClass` when class with constant. ([@ydah])
- Fix a false positive for `RSpec/ExampleWithoutDescription` when `specify` with multi-line block and missing description. ([@ydah])
- Fix an incorrect autocorrect for `RSpec/ChangeByZero` when compound expectations with line break before `.by(0)`. ([@ydah])

## 2.26.1 (2024-01-05)

Expand Down
28 changes: 27 additions & 1 deletion lib/rubocop/cop/rspec/change_by_zero.rb
Expand Up @@ -59,6 +59,8 @@ module RSpec
#
class ChangeByZero < Base
extend AutoCorrector
include RangeHelp

MSG = 'Prefer `not_to change` over `to %<method>s.by(0)`.'
MSG_COMPOUND = 'Prefer %<preferred>s with compound expectations ' \
'over `%<method>s.by(0)`.'
Expand Down Expand Up @@ -140,8 +142,32 @@ def autocorrect_compound(corrector, node)

change_nodes(node) do |change_node|
corrector.replace(change_node.loc.selector, negated_matcher)
range = node.loc.dot.with(end_pos: node.source_range.end_pos)
insert_operator(corrector, node, change_node)
remove_by_zero(corrector, node, change_node)
end
end

def insert_operator(corrector, node, change_node)
operator = node.right_siblings.first
return unless %i[& |].include?(operator)

corrector.insert_after(
replace_node(node, change_node), " #{operator}"
)
end

def replace_node(node, change_node)
expect_change_with_arguments(node) ? change_node : change_node.parent
end

def remove_by_zero(corrector, node, change_node)
range = node.loc.dot.with(end_pos: node.source_range.end_pos)
if change_node.loc.line == range.line
corrector.remove(range)
else
corrector.remove(
range_by_whole_lines(range, include_final_newline: true)
)
end
end

Expand Down
68 changes: 68 additions & 0 deletions spec/rubocop/cop/rspec/change_by_zero_spec.rb
Expand Up @@ -258,6 +258,74 @@
end
RUBY
end

it 'registers an offense and autocorrect when ' \
'the argument to `by` is zero with compound expectations (`and`/`or`) ' \
'with line break before `.by(0)`' do
expect_offense(<<~RUBY)
it do
expect { foo }
.to change(Foo, :bar)
^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
.and change(Foo, :baz)
^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
expect { foo }
.to change { Foo.bar }
^^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
.or change { Foo.baz }
^^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
end
RUBY

expect_correction(<<~RUBY)
it do
expect { foo }
.to not_change(Foo, :bar)
.and not_change(Foo, :baz)
expect { foo }
.to not_change { Foo.bar }
.or not_change { Foo.baz }
end
RUBY
end

it 'registers an offense and autocorrect when ' \
'the argument to `by` is zero with compound expectations (`&`/`|`) ' \
'with line break before `.by(0)`' do
expect_offense(<<~RUBY)
it do
expect { foo }
.to change(Foo, :bar)
^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0) &
change(Foo, :baz)
^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
expect { foo }
.to change { Foo.bar }
^^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0) |
change { Foo.baz }
^^^^^^^^^^^^^^^^^^ Prefer `not_change` with compound expectations over `change.by(0)`.
.by(0)
end
RUBY

expect_correction(<<~RUBY)
it do
expect { foo }
.to not_change(Foo, :bar) &
not_change(Foo, :baz)
expect { foo }
.to not_change { Foo.bar } |
not_change { Foo.baz }
end
RUBY
end
end

it 'does not register an offense when the argument to `by` is not zero' do
Expand Down

0 comments on commit 91b1624

Please sign in to comment.