Skip to content

Commit

Permalink
Add RSpec/Rails/TravelAround cop
Browse files Browse the repository at this point in the history
  • Loading branch information
r7kamura committed Dec 2, 2022
1 parent f8a5c4a commit 0e58c9a
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Add `RSpec/DuplicatedMetadata` cop. ([@r7kamura])
- Mark `RSpec/BeEql` as `Safe: false`. ([@r7kamura])
- Add `RSpec/RedundantAround` cop. ([@r7kamura])
- Add `RSpec/Rails/TravelAround` cop. ([@r7kamura])

## 2.15.0 (2022-11-03)

Expand Down
7 changes: 7 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1044,3 +1044,10 @@ RSpec/Rails/HttpStatus:
VersionAdded: '1.23'
VersionChanged: '2.0'
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/HttpStatus

RSpec/Rails/TravelAround:
Description: Prefer to travel in `before` rather than `around`.
Enabled: pending
SafeAutoCorrect: false
VersionAdded: "<<next>>"
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/TravelAround
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,6 @@
* xref:cops_rspec_rails.adoc#rspecrails/havehttpstatus[RSpec/Rails/HaveHttpStatus]
* xref:cops_rspec_rails.adoc#rspecrails/httpstatus[RSpec/Rails/HttpStatus]
* xref:cops_rspec_rails.adoc#rspecrails/inferredspectype[RSpec/Rails/InferredSpecType]
* xref:cops_rspec_rails.adoc#rspecrails/travelaround[RSpec/Rails/TravelAround]

// END_COP_LIST
38 changes: 38 additions & 0 deletions docs/modules/ROOT/pages/cops_rspec_rails.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,41 @@ end
=== References

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/InferredSpecType

== RSpec/Rails/TravelAround

|===
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed

| Pending
| Yes
| Yes (Unsafe)
| <<next>>
| -
|===

Prefer to travel in `before` rather than `around`.

=== Safety

This cop's autocorrection is unsafe because the order of execution
will change if other steps exist before traveling in `around`.

=== Examples

[source,ruby]
----
# bad
around do |example|
freeze_time do
example.run
end
end
# good
before { freeze_time }
----

=== References

* https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Rails/TravelAround
79 changes: 79 additions & 0 deletions lib/rubocop/cop/rspec/rails/travel_around.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
module Rails
# Prefer to travel in `before` rather than `around`.
#
# @safety
# This cop's autocorrection is unsafe because the order of execution
# will change if other steps exist before traveling in `around`.
#
# @example
# # bad
# around do |example|
# freeze_time do
# example.run
# end
# end
#
# # good
# before { freeze_time }
class TravelAround < Base
extend AutoCorrector

MSG = 'Prefer to travel in `before` rather than `around`.'

TRAVEL_METHOD_NAMES = Set.new(
%i[
freeze_time
travel
travel_to
]
)

def on_block(node)
send_node = match_run_in_travel(node)
return unless send_node

add_offense(node) do |corrector|
autocorrect(corrector, node, send_node)
end
end
alias on_numblock on_block

private

# @!method match_run_in_travel(node)
def_node_matcher :match_run_in_travel, <<~PATTERN
(block
$(send nil? TRAVEL_METHOD_NAMES ...)
(args ...)
(send _ :run)
)
PATTERN

def autocorrect(corrector, node, send_node)
corrector.replace(
node,
node.body.source
)
corrector.insert_before(
extract_surrounding_around_block(node),
"before { #{send_node.source} }\n\n"
)
end

# @param node [RuboCop::AST::BlockNode]
# @return [RuboCop::AST::BlockNode, nil]
def extract_surrounding_around_block(node)
node.each_ancestor(:block).find do |ancestor|
ancestor.method?(:around)
end
end
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/rspec_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# Rails/HttpStatus cannot be loaded if rack/utils is unavailable.
end
require_relative 'rspec/rails/inferred_spec_type'
require_relative 'rspec/rails/travel_around'

require_relative 'rspec/align_left_let_brace'
require_relative 'rspec/align_right_let_brace'
Expand Down
99 changes: 99 additions & 0 deletions spec/rubocop/cop/rspec/rails/travel_around_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::RSpec::Rails::TravelAround do
context 'with `freeze_time` in `before`' do
it 'registers no offense' do
expect_no_offenses(<<~RUBY)
before { freeze_time }
RUBY
end
end

context 'with `freeze_time` in `around`' do
it 'registers offense' do
expect_offense(<<~RUBY)
around do |example|
freeze_time do
^^^^^^^^^^^^^^ Prefer to travel in `before` rather than `around`.
example.run
end
end
RUBY

expect_correction(<<~RUBY)
before { freeze_time }
around do |example|
example.run
end
RUBY
end
end

context 'with `freeze_time` and another node in `around`' do
it 'registers offense' do
expect_offense(<<~RUBY)
around do |example|
foo
freeze_time do
^^^^^^^^^^^^^^ Prefer to travel in `before` rather than `around`.
example.run
end
end
RUBY

expect_correction(<<~RUBY)
before { freeze_time }
around do |example|
foo
example.run
end
RUBY
end
end

context 'with `travel` in `around`' do
it 'registers offense' do
expect_offense(<<~RUBY)
around do |example|
travel(duration) do
^^^^^^^^^^^^^^^^^^^ Prefer to travel in `before` rather than `around`.
example.run
end
end
RUBY

expect_correction(<<~RUBY)
before { travel(duration) }
around do |example|
example.run
end
RUBY
end
end

context 'with `travel_to` in `around`' do
it 'registers offense' do
expect_offense(<<~RUBY)
around do |example|
travel_to(time) do
^^^^^^^^^^^^^^^^^^ Prefer to travel in `before` rather than `around`.
example.run
end
end
RUBY

expect_correction(<<~RUBY)
before { travel_to(time) }
around do |example|
example.run
end
RUBY
end
end
end

0 comments on commit 0e58c9a

Please sign in to comment.