# Used to see if a specified TML structure (given in the form of a Hash) resembles the # overall structure of the given TML document. # # This is used to validate that the document conforms to a particular set of expectations # without the overhead of loading it into the simulator. # # This class is tied into RSpec with the #resemble_tml method, as well as into Test::Unit # with the #assert_tml_resembles method: # # RSpec Example # # get :index # response.should resemble_tml(:tml => { :screen => [ :setvar, :submit => { :getvar } ] }) # # response.should_not resemble_tml(. . .) # negative test # # Test::Unit Examples # # get :index # assert_tml_resembles(:tml => { :screen => [ :setvar, :submit => { :getvar } ] }, response) # # assert_tml_not_resemble(. . .) # negative test # class Rtml::Test::ResemblanceTest attr_reader :tml, :expectation def pass? pass_recursively? end def initialize(expectation, tml) build_expectation(expectation) find_tml(tml) @xml = Hpricot::XML(@tml).root end private def pass_recursively?(exp = @expectation.deep_dup, level = @xml) case exp when Hash pass_recursively_with_hash?(exp, level) when Array pass_with_array?(exp, level) else pass_with_object?(exp, level) end end def pass_recursively_with_hash?(exp, level) if key_matches = pass_with_array?(exp.keys, level) key_matches.each do |match| if array = pass_with_array?(exp.values, match) return array end end end false end def pass_with_array?(exp, level) matches = [] exp.each do |item| return false unless object_matches = pass_recursively?(item, level) matches.concat object_matches end matches end def pass_with_object?(exp, level) if exp.to_s == level.name [level] elsif (matches = (level / exp)) && !matches.empty? matches else false end end def build_expectation(hash) @expectation = hash end def find_tml(object) @tml = case object when ActionController::TestResponse object.body when String object when Hpricot::Doc, Hpricot::Elem object.to_s when ActiveRecord::Base, Rtml::DocumentModelObject object.to_tml else object.to_s end end end