lib/minitest_to_rspec/input/subprocessors/base.rb
# frozen_string_literal: true
require 'minitest_to_rspec/sexp_assertions'
module MinitestToRspec
module Input
module Subprocessors
# Parent class of "sub-processors". There is one sub-processor for each
# `sexp_type` that `Processor` knows how to process.
#
# For example, `Subprocessors::Call` will process an `s(:call, ..)`
# expression representing minitest code, and return an S-expression
# representing equivalent RSpec code.
class Base
include SexpAssertions
def initialize(rails, mocha)
@rails = rails
@mocha = mocha
end
# Returns a s-expression representing an rspec-mocks stub.
def allow_to(msg_recipient, matcher, any_instance = false)
allow_method = any_instance ? :allow_any_instance_of : :allow
target = s(:call, nil, allow_method, msg_recipient)
s(:call, target, :to, matcher)
end
# Returns a s-expression representing an RSpec expectation, i.e. the
# combination of an "expectation target" and a matcher.
def expect(target, eager, phase, matcher, any_instance)
et = expectation_target(target, eager, any_instance)
s(:call, et, phase, matcher)
end
def expect_to(matcher, target, eager, any_instance = false)
expect(target, eager, :to, matcher, any_instance)
end
def expect_to_not(matcher, target, eager)
expect(target, eager, :to_not, matcher, false)
end
# In RSpec, `expect` returns an "expectation target". This
# can be based on an expression, as in `expect(1 + 1)` or it
# can be based on a block, as in `expect { raise }`. Either
# way, it's called an "expectation target".
def expectation_target(exp, eager, any_instance)
if eager
expectation_target_eager(exp, any_instance)
else
expectation_target_lazy(exp)
end
end
def expectation_target_eager(exp, any_instance)
expect_method = any_instance ? :expect_any_instance_of : :expect
s(:call, nil, expect_method, exp)
end
def expectation_target_lazy(block)
s(:iter,
s(:call, nil, :expect),
0,
full_process(block)
)
end
# If it's a `Sexp`, run `obj` through a new `Processor`. Otherwise,
# return `obj`.
#
# This is useful for expressions that cannot be fully understood by a
# single subprocessor. For example, we must begin processing all :iter
# expressions, because some :iter represent calls we're interested in,
# e.g. `assert_difference`. However, if the :iter turns out to be
# uninteresting (perhaps it has no assertions) we still want to fully
# process its sub-expressions.
#
# TODO: `full_process` may not be the best name.
def full_process(obj)
obj.is_a?(Sexp) ? Processor.new(@rails, @mocha).process(obj) : obj
end
def matcher(name, *args)
exp = s(:call, nil, name)
exp.concat(args)
end
end
end
end
end