Skip to content

Commit

Permalink
Add cop for time in expect
Browse files Browse the repository at this point in the history
  • Loading branch information
RitamBanerjee committed Dec 18, 2023
1 parent d42bd08 commit 9db62cb
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/ezcater_rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
require "rubocop/cop/ezcater/require_custom_error"
require "rubocop/cop/ezcater/require_gql_error_helpers"
require "rubocop/cop/ezcater/rspec_match_ordered_array"
require "rubocop/cop/ezcater/rspec_assert_on_current_time"
require "rubocop/cop/ezcater/rspec_require_browser_mock"
require "rubocop/cop/ezcater/rspec_require_feature_flag_mock"
require "rubocop/cop/ezcater/rspec_require_http_status_matcher"
Expand Down
54 changes: 54 additions & 0 deletions lib/rubocop/cop/ezcater/rspec_assert_on_current_time.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Ezcater
# Enforce not using Time in an assertion
#
# @example
#
# # good
# let(:current_time) { Time.zone.now } [outside of test]
# expect(foo).to eq(current_time)
#
# # bad
# expect(foo).to eq(Time.zone.now)
# expect(foo).to eq({a: { b: Time.zone.now }})
class RspecAssertOnCurrentTime < Cop
MSG = "Do not call Time in an assertion. Memoize the value outside the spec and use that instead."

def_node_matcher "expect", <<~PATTERN
(send (send nil? :expect (...)) :to $...)
PATTERN

def_node_matcher "time", <<~PATTERN
(:const nil? :Time)
PATTERN

def on_send(node)
expect(node) do |expect_input|
expect_input.each do |input|
recursively_look_for_time(input)
end
end
end

def recursively_look_for_time(node)
return unless node.is_a?(RuboCop::AST::Node)

node&.children&.each do |child|
next unless child.respond_to?(:children)

time(child) do
add_offense(child, location: :expression, message: MSG)
end

child.children.each do |inner_child|
recursively_look_for_time(inner_child)
end
end
end
end
end
end
end
42 changes: 42 additions & 0 deletions spec/rubocop/cop/ezcater/rspec_assert_on_current_time_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# encoding utf-8
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Ezcater::RspecAssertOnCurrentTime, :config do
subject(:cop) { described_class.new(config) }

it "corrects when using Time inside expect" do
source = "expect(foo).to match(Time.zone.now)"

inspect_source(source)

expect(cop.offenses).not_to be_empty
expect(cop.highlights).to match_array("Time")
expect(cop.messages).to match_array(described_class::MSG)
end

it "corrects when using Time nested inside expect" do
source = "expect(foo).to match({a: { b: Time.zone.now }})"

inspect_source(source)

expect(cop.offenses).not_to be_empty
expect(cop.highlights).to match_array("Time")
expect(cop.messages).to match_array(described_class::MSG)
end

it "accepts when using expect without a call to Time" do
source = "expect(foo).to match(current_time)"

inspect_source(source)

expect(cop.offenses).to be_empty
end

it "accepts when using Time outside of expect" do
source = "let(:a_time) { Time.zone.now }"

inspect_source(source)

expect(cop.offenses).to be_empty
end
end

0 comments on commit 9db62cb

Please sign in to comment.