require 'rubocop'
module Helpers
# Shoulda => Spec
BLOCK_KEYWORD_MAPPINGS = { should: :it,
context: :describe,
setup: :before,
teardown: :after }
MATCHERS_KEYWORDS = %w[should should_not should_eventually].map(&:to_sym)
def node_call(node)
_, call, *_ = node.to_a
call
end
def meth_call?(node)
h, *_ = node.to_a
h.nil?
end
def node_args(node)
_, _, *args = node.to_a
args
end
def shoulda_block_node?(node)
call = node_call(node)
first_arg = node_args(node).first
BLOCK_KEYWORD_MAPPINGS.keys.include?(call) &&
node.parent.block_type? &&
first_arg &&
first_arg.type == :str
end
def shoulda_meth_node?(node)
call = node_call(node)
MATCHERS_KEYWORDS.include?(call) &&
!shoulda_block_node?(node) &&
meth_call?(node)
end
def matchy_node?(node)
_, call = node.to_a
MATCHERS_KEYWORDS.include?(call) &&
node.parent.send_type?
end
def matchy_call(node)
_, call = node.to_a
call
end
end
class MatchersCop < RuboCop::Cop::Cop
include Helpers
def self.cop_type
:magic
end
def on_send(node)
add_offense(node, :expression) if shoulda_meth_node?(node)
end
def autocorrect(node)
lambda do |corrector|
location = node.loc.expression
corrector.replace(location, convert_to_it(node))
end
end
def message(node)
"Do not use `#{node_call(node)}` in a test"
end
private
def convert_to_it(node)
_, call, matcher = node.to_a
matcher_str = Unparser.unparse(matcher)
case call
when :should
"# it { assert #{matcher_str} } # REMOVE_ME"
when :should_not
"# it { refute #{matcher_str} } # REMOVE_ME"
end
end
end