class RSpec::JsonExpectations::JsonTraverser

This class allows to traverse a json actual value along with json expected value for inclusion and check if they match. Errors are accumulated in errors hash for each json atom paths.

Constants

HANDLED_BY_SIMPLE_VALUE_HANDLER
RSPECMATCHERS
SUPPORTED_VALUES

Public Class Methods

traverse(errors, expected, actual, negate=false, prefix=[], options={}) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 17
def traverse(errors, expected, actual, negate=false, prefix=[], options={})
  [
    handle_hash(errors, expected, actual, negate, prefix),
    handle_array(errors, expected, actual, negate, prefix),
    handle_unordered(errors, expected, actual, negate, prefix, options),
    handle_value(errors, expected, actual, negate, prefix),
    handle_regex(errors, expected, actual, negate, prefix),
    handle_rspec_matcher(errors, expected, actual, negate, prefix),
    handle_unsupported(expected)
  ].any?
end

Private Class Methods

conditionally_negate(value, negate=false) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 158
def conditionally_negate(value, negate=false)
  value ^ negate
end
fetch(actual, key, default=nil) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 148
def fetch(actual, key, default=nil)
  if actual.is_a?(Hash)
    actual.has_key?(key) ? actual[key] : actual[key.to_s]
  elsif actual.is_a?(Array)
    actual[key]
  else
    default
  end
end
handle_array(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 49
def handle_array(errors, expected, actual, negate=false, prefix=[])
  return nil unless expected.is_a?(Array)

  transformed_expected = expected.each_with_index.map { |v, k| [k, v] }
  handle_keyvalue(errors, transformed_expected, actual, negate, prefix)
end
handle_hash(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 43
def handle_hash(errors, expected, actual, negate=false, prefix=[])
  return nil unless expected.is_a?(Hash)

  handle_keyvalue(errors, expected, actual, negate, prefix)
end
handle_keyvalue(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 31
def handle_keyvalue(errors, expected, actual, negate=false, prefix=[])
  expected.map do |key, value|
    new_prefix = prefix + [key]
    if has_key?(actual, key)
      traverse(errors, value, fetch(actual, key), negate, new_prefix)
    else
      errors[new_prefix.join("/")] = :no_key unless negate
      conditionally_negate(false, negate)
    end
  end.all? || false
end
handle_regex(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 101
def handle_regex(errors, expected, actual, negate=false, prefix=[])
  return nil unless expected.is_a?(Regexp)

  if conditionally_negate(!!expected.match(actual.to_s), negate)
    true
  else
    errors[prefix.join("/")] = {
      actual: actual,
      expected: expected
    }
    false
  end
end
handle_rspec_matcher(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 115
def handle_rspec_matcher(errors, expected, actual, negate=false, prefix=[])
  return nil unless defined?(RSpec::Matchers)
  return nil unless expected.is_a?(RSpec::Matchers::BuiltIn::BaseMatcher) ||
    expected.is_a?(RSpec::Matchers::AliasedMatcher)

  if conditionally_negate(!!expected.matches?(actual), negate)
    true
  else
    errors[prefix.join("/")] = {
      actual: actual,
      expected: expected.description
    }
    false
  end
end
handle_unordered(errors, expected, actual, negate=false, prefix=[], options={}) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 56
def handle_unordered(errors, expected, actual, negate=false, prefix=[], options={})
  return nil unless expected.is_a?(Matchers::UnorderedArrayMatcher)

  match_size_assertion_result = \
    match_size_of_collection(errors, expected, actual, prefix, options)

  if match_size_assertion_result == false
    conditionally_negate(false, negate)
  else
    conditionally_negate(expected.match(errors, actual, prefix), negate)
  end
end
handle_unsupported(expected) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 131
def handle_unsupported(expected)
  unless SUPPORTED_VALUES.any? { |type| expected.is_a?(type) }
    raise NotImplementedError,
      "#{expected} expectation is not supported"
  end
end
handle_value(errors, expected, actual, negate=false, prefix=[]) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 83
def handle_value(errors, expected, actual, negate=false, prefix=[])
  return nil unless handled_by_simple_value?(expected)

  if conditionally_negate(actual == expected, negate)
    true
  else
    errors[prefix.join("/")] = {
      actual: actual,
      expected: expected
    }
    false
  end
end
handled_by_simple_value?(expected) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 97
def handled_by_simple_value?(expected)
  HANDLED_BY_SIMPLE_VALUE_HANDLER.any? { |type| type === expected }
end
has_key?(actual, key) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 138
def has_key?(actual, key)
  if actual.is_a?(Hash)
    actual.has_key?(key) || actual.has_key?(key.to_s)
  elsif actual.is_a?(Array)
    actual.count > key
  else
    false
  end
end
match_size_of_collection(errors, expected, actual, prefix, options) click to toggle source
# File lib/rspec/json_expectations/json_traverser.rb, line 69
def match_size_of_collection(errors, expected, actual, prefix, options)
  match_size = options[:match_size]
  return nil unless match_size
  return nil if expected.size == actual.size

  errors[prefix.join("/")] = {
    _size_mismatch_error: true,
    expected: expected.unwrap_array,
    actual: actual,
    extra_elements: actual - expected.unwrap_array,
  }
  false
end