lib/rspec/sorbet/instance_doubles.rb in rspec-sorbet-1.0.0 vs lib/rspec/sorbet/instance_doubles.rb in rspec-sorbet-1.1.0

- old
+ new

@@ -3,15 +3,10 @@ require 'sorbet-runtime' module RSpec module Sorbet module InstanceDoubles - WHITELISTED_ERROR_MESSAGES = [ - 'RSpec::Mocks::InstanceVerifyingDouble', - 'InstanceDouble' - ].freeze - def allow_instance_doubles! T::Configuration.inline_type_error_handler = proc do |error| inline_type_error_handler(error) end @@ -20,21 +15,76 @@ end end private + INLINE_INSTANCE_DOUBLE_REGEX = + /T.let: Expected type (T.any\()?(?<expected_classes>[a-zA-Z:: ,]*)(\))?, got type (.*) with value #<InstanceDouble\((?<doubled_module>[a-zA-Z:: ,]*)\)/.freeze + def inline_type_error_handler(error) - raise error unless error.is_a?(TypeError) && message_is_whitelisted?(error.message) + case error + when TypeError + message = error.message + return if instance_double_message_with_ellipsis?(message) || typed_array_message?(message) + + _, expected_types_string, doubled_module_string = (message.match(INLINE_INSTANCE_DOUBLE_REGEX) || [])[0..2] + raise error unless expected_types_string && doubled_module_string + + expected_types = expected_types_string.split(',').map do |expected_type_string| + Object.const_get(expected_type_string.strip) + end + doubled_module = Object.const_get(doubled_module_string) + + valid = expected_types.any? do |expected_type| + doubled_module.ancestors.include?(expected_type) + end + + raise error unless valid + else + raise error + end end - def call_validation_error_handler(_signature, opts) - raise TypeError, opts[:pretty_message] unless message_is_whitelisted?(opts[:message]) + INSTANCE_VERIFYING_DOUBLE_OR_INSTANCE_DOUBLE = + /(RSpec::Mocks::InstanceVerifyingDouble|InstanceDouble)/.freeze + + def instance_double_message_with_ellipsis?(message) + message.include?('...') && message.match?(INSTANCE_VERIFYING_DOUBLE_OR_INSTANCE_DOUBLE) end - def message_is_whitelisted?(message) - WHITELISTED_ERROR_MESSAGES.any? do |whitelisted_message| - message.include?(whitelisted_message) + TYPED_ARRAY_MESSAGE = /got T::Array/.freeze + + def typed_array_message?(message) + message.match?(TYPED_ARRAY_MESSAGE) + end + + def call_validation_error_handler(_signature, opts) + should_raise = true + + if opts[:pretty_message].match?(INSTANCE_VERIFYING_DOUBLE_OR_INSTANCE_DOUBLE) + typing = opts[:type] + value = opts[:value].is_a?(Array) ? opts[:value].first : opts[:value] + target = value.instance_variable_get(:@doubled_module).target + + case typing + when T::Types::TypedArray + typing = typing.type + end + + case typing + when T::Types::Simple + should_raise = !target.ancestors.include?(typing.raw_type) + when T::Types::Union + valid = typing.types.map(&:raw_type).any? do |type| + target.ancestors.include?(type) + end + should_raise = !valid + else + should_raise = !target.ancestors.include?(typing) + end end + + raise TypeError, opts[:pretty_message] if should_raise end end end end