require 'test_helper' require 'nokogiri' class AtomResultsTest < ActionView::TestCase include ActionView::Helpers::UrlHelper @@namespaces = { "atom" => "http://www.w3.org/2005/Atom", "opensearch" => "http://a9.com/-/spec/opensearch/1.1/", "prism" => "http://prismstandard.org/namespaces/basic/2.1/", "dcterms" => "http://purl.org/dc/terms/", "bibo" => "http://purl.org/ontology/bibo/" } # Instead of using assert_select, we do it ourselves with nokogiri # for better namespace control. # # xml = Nokogiri::XML( rendered ) # assert_node(xml, "atom:entry") do |matched_nodes| # assert matched_node.first["attribute"] == "foo" # end def assert_node(xml, xpath, options = {}) result = xml.xpath(xpath, @@namespaces) assert result.length > 0, "Expected xpath '#{xpath}' to match in #{xml.to_s[0..200]}..." if options[:text] assert_equal options[:text], result.text.strip, "Expected #{options[:text]} as content of #{result.to_s[0..200]}" end yield result if block_given? end def setup @total_items = 1000 @start = 6 @per_page = 15 @engine = BentoSearch::MockEngine.new(:total_items => @total_items) @results = @engine.search("some query", :start => @start, :per_page => @per_page) # but fill the first result elements with some non-blank data to test @article = BentoSearch::ResultItem.new( :title => "An Article", # :link => "http://example.org/main_link", # :unique_id => "UNIQUE_ID", :format => "Article", # :format_str => "Uncontrolled format", # :language_code => "en", # :year => "2004", # :volume => "10", # :issue => "1", # :start_page => "101", # :end_page => "110", # :source_title => "Journal of Something", # :issn => "12345678", # :doi => "10.1000/182", # :abstract => "This is an abstract with escaped > parts < ", # :authors => [ # BentoSearch::Author.new(:first => "John", :last => "Smith"), BentoSearch::Author.new(:display => "Jones, Jane") ], :other_links => [ # BentoSearch::Link.new(:url => "http://example.org/bare_link"), BentoSearch::Link.new( :url => "http://example.org/label_and_type", :label => "A link somewhere", :type => "application/pdf" ), BentoSearch::Link.new( :url => "http://example.org/rel", :rel => "something" ) ] ) @article_with_html_abstract = BentoSearch::ResultItem.new( :title => "foo", :format => "Article", :abstract => "This is html".html_safe ) @article_with_full_date = BentoSearch::ResultItem.new( :title => "foo", :format => "Article", :publication_date => Date.new(2011, 5, 6) ) @book = BentoSearch::ResultItem.new( :title => "A Book", :format => "Book", :publisher => "Some publisher", :isbn => "123456789X", :oclcnum => "12124345", :year => "2004" ) @results[0] = @article @results[1] = @article_with_html_abstract @results[3] = @article_with_full_date @results[4] = @book end def test_smoke_atom_validate # Validate under Atom schema. Should we validate under prism and dc schemas # too? Not sure if it makes sense, or if there's even a relevant schema # for how we're using em. So of just basic 'smoke' value. render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) { |config| config.strict } atom_xsd_filepath = File.expand_path("../../support/atom.xsd.xml", __FILE__) schema_xml = Nokogiri::XML(File.open(atom_xsd_filepath)) # modify to add processContents lax so it'll let us include elements from # external namespaces. schema_xml.xpath("//xs:any[@namespace='##other']", {"xs" => "http://www.w3.org/2001/XMLSchema"}).each do |node| node["processContents"] = "lax" end schema = Nokogiri::XML::Schema.from_document( schema_xml ) assert_empty schema.validate(xml_response), "Validates with atom XSD schema" end def test_feed_metadata render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) assert_node(xml_response, "atom:feed") do |feed| assert_node(feed, "atom:title") assert_node(feed, "atom:author") assert_node(feed, "atom:updated") assert_node(feed, "opensearch:totalResults", :text => @total_items.to_s) assert_node(feed, "opensearch:startIndex", :text => @start.to_s) assert_node(feed, "opensearch:itemsPerPage", :text => @per_page.to_s) end end def test_article_entry_example render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) assert_node(xml_response, "./atom:feed/atom:entry[1]") do |article| assert_node(article, "atom:title", :text => @article.title) assert_node(article, "prism:coverDate", :text => @article.year) assert_node(article, "prism:issn", :text => @article.issn) assert_node(article, "prism:doi", :text => @article.doi) assert_node(article, "prism:volume", :text => @article.volume) assert_node(article, "prism:number", :text => @article.issue) assert_node(article, "prism:startingPage", :text => @article.start_page) assert_node(article, "prism:endingPage", :text => @article.end_page) assert_node(article, "prism:publicationName", :text => @article.source_title) abstract = article.at_xpath("atom:summary", @@namespaces) assert_present abstract, "Has an abstract" assert_equal "text", abstract["type"], "Abstract type text" assert_equal @article.abstract, abstract.text assert_node(article, "dcterms:language[@vocabulary='http://dbpedia.org/resource/ISO_639-1']", :text => @article.language_iso_639_1) assert_node(article, "dcterms:language[@vocabulary='http://dbpedia.org/resource/ISO_639-3']", :text => @article.language_iso_639_3) assert_node(article, "dcterms:language[not(@vocabulary)]", :text => @article.language_str) assert_node(article, "dcterms:type[not(@vocabulary)]", :text => @article.format_str) assert_node(article, "dcterms:type[@vocabulary='http://schema.org/']", :text => @article.schema_org_type_url) assert_node(article, "dcterms:type[@vocabulary='http://purl.org/NET/bento_search/ontology']", :text => @article.format) # Just make sure right number of author elements, with right structure. assert_node(article, "atom:author/atom:name") do |authors| assert_equal @article.authors.length, authors.length, "right number of author elements" end # Links. Main link is just rel=alternate assert_node(article, "atom:link[@rel='alternate'][@href='#{@article.link}']") # other links also there, default rel=related assert_node(article, "atom:link[@rel='related'][@type='application/pdf'][@title='A link somewhere'][@href='http://example.org/label_and_type']") assert_node(article, "atom:link[@rel='something'][@href='http://example.org/rel']") end end def test_with_unique_id @results = @engine.search("find") @results[0] = BentoSearch::ResultItem.new( :title => "Something", :unique_id => "a000:/01", :engine_id => "some_engine" ) render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) with_unique_id = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[0] assert_node(with_unique_id, "atom:id") do |id| # based off of engine_id and unique_id assert_includes id.text, "some_engine" assert_includes id.text, "a000%3A%2F01" end end def test_with_html_abstract render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) with_html_abstract = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[1] assert_node(with_html_abstract, "atom:summary[@type='html']", :text => @article_with_html_abstract.abstract.to_s) end def test_book render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) book = xml_response.xpath("./atom:feed/atom:entry", @@namespaces)[4] assert_node(book, "dcterms:type[@vocabulary='http://purl.org/NET/bento_search/ontology']", :text => "Book") assert_node(book, "dcterms:type[@vocabulary='http://schema.org/']", :text => "http://schema.org/Book") assert_node(book, "dcterms:publisher", :text => @book.publisher) assert_node(book, "prism:isbn", :text => @book.isbn) assert_node(book, "bibo:oclcnum", :text => @book.oclcnum) end def test_with_full_date render :template => "bento_search/atom_results", :locals => {:atom_results => @results} xml_response = Nokogiri::XML( rendered ) with_full_date = xml_response.at_xpath("./atom:feed/atom:entry[4]", @@namespaces) assert_node(with_full_date, "prism:coverDate", :text => "2011-05-06") end def test_nil_results # should render a more or less empty atom response for # nil results, convenient to not raise on nil render :template => "bento_search/atom_results", :locals => {:atom_results => nil} end def test_locals_for_feed_name_and_author render( :template => "bento_search/atom_results", :locals => {:atom_results => @results, :feed_name => "My Feed", :feed_author_name => "ACME Seed And Feed Products"} ) xml_response = Nokogiri::XML( rendered ) assert_node(xml_response, "./atom:feed/atom:title", :text => "My Feed") assert_node(xml_response, "./atom:feed/atom:author/atom:name", :text => "ACME Seed And Feed Products") end def test_html_in_title_stripped results = BentoSearch::Results.new results << BentoSearch::ResultItem.new( :title => "html title".html_safe ) render(:template => "bento_search/atom_results", :locals => {:atom_results => results}) xml_response = Nokogiri::XML( rendered ) assert_node(xml_response, "./atom:feed/atom:entry[1]/atom:title", :text => "html title") end end