Module: AssertXPath
- Included in:
- AssertJavaScript
- Defined in:
- lib/assert_xpath.rb,
lib/assert_xpath.rb
Defined Under Namespace
Modules: CommonXPathExtensions Classes: HpricotHelper, LibxmlHelper, RexmlHelper, XmlHelper
Constant Summary collapse
- Element =
:nodoc:
::REXML::Element
Instance Method Summary collapse
-
#assert_any_xpath(xpath, matcher = nil, diagnostic = nil, &block) ⇒ Object
Search nodes for a matching XPath whose
AssertXPath::Element#inner_text
matches a Regular Expression. -
#assert_hpricot(*args, &block) ⇒ Object
%html <a name=‘assert_hpricot’></a>.
-
#assert_libxml(*args, &block) ⇒ Object
ERGO more documentation!.
-
#assert_rexml(*args, &block) ⇒ Object
Processes a string of text, or the hidden
@response.body
, using REXML, and sets the hidden@xdoc
node. -
#assert_tag_id(tag, id = {}, diagnostic = nil, diagnostic2 = nil, &block) ⇒ Object
Wraps the common idiom
assert_xpath('descendant-or-self::./my_tag[ @id = "my_id" ]')
. -
#assert_tidy(messy = @response.body, verbosity = :noisy) ⇒ Object
%html <a name=‘assert_tidy’></a> Thin wrapper on the Tidy command line program (the one released 2005 September) *
messy
- optional string containing messy HTML. -
#assert_xml(*args, &block) ⇒ Object
%html <a name=‘assert_xml’></a>.
-
#assert_xpath(*args, &block) ⇒ Object
%html <a name=‘assert_xpath’></a>.
-
#deny_any_xpath(xpath, matcher, diagnostic = nil) ⇒ Object
Negates
assert_any_xpath
. -
#deny_tag_id(tag, id, diagnostic = nil, diagnostic2 = nil) ⇒ Object
Negates
assert_tag_id
. -
#deny_xpath(*args) ⇒ Object
Negates
assert_xpath
. -
#drill(&block) ⇒ Object
ERGO document me.
-
#indent_xml(doc = @xdoc || assert_xml) ⇒ Object
Pretty-print a REXML::Element or Hpricot::Elem *
doc
- optional element. -
#invoke_hpricot ⇒ Object
Subsequent
assert_xml
calls will use Hpricot. -
#invoke_libxml(favorite_flavor = :html) ⇒ Object
Subsequent
assert_xml
calls will use LibXML. -
#invoke_rexml ⇒ Object
Subsequent
assert_xml
calls will use REXML. - #using(kode) ⇒ Object
-
#validate_as(type) ⇒ Object
Temporarily sets the validation type to :xml, :html, or :xhtml.
Instance Method Details
#assert_any_xpath(xpath, matcher = nil, diagnostic = nil, &block) ⇒ Object
Search nodes for a matching XPath whose AssertXPath::Element#inner_text
matches a Regular Expression. Depends on assert_xml
-
xpath
- a query string describing a path among XML nodes. See: XPath Tutorial Roundup -
matcher
- optional Regular Expression to test node contents -
diagnostic
- optional string to add to failure message -
block|node|
- optional block called once per match. If this block returns a value other thanfalse
ornil
, assert_any_xpath stops looping and returns the currentnode
Example: %transclude AssertXPathSuite#test_assert_any_xpath
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 |
# File 'lib/assert_xpath.rb', line 491 def assert_any_xpath(xpath, matcher = nil, diagnostic = nil, &block) matcher ||= // block ||= lambda{ true } found_any = false found_match = false xpath = symbol_to_xpath(xpath) stash_xdoc do #assert_xpath xpath, diagnostic if !using(:rexml?) @xdoc.search(xpath) do |@xdoc| found_any = true if @xdoc.inner_text =~ matcher found_match = true _bequeath_attributes(@xdoc) return @xdoc if block.call(@xdoc) # note we only exit block if block.nil? or call returns false end end else # ERGO merge! @xdoc.each_element(xpath) do |@xdoc| found_any = true if @xdoc.inner_text =~ matcher found_match = true _bequeath_attributes(@xdoc) return @xdoc if block.call(@xdoc) # note we only exit block if block.nil? or call returns false end end end end found_any or flunk_xpath(diagnostic, "should find xpath <#{_esc xpath}>") found_match or flunk_xpath( diagnostic, "can find xpath <#{_esc xpath}> but can't find pattern <?>", matcher ) end |
#assert_hpricot(*args, &block) ⇒ Object
%html <a name=‘assert_hpricot’></a>
This parses one XML string using Hpricot, so subsequent calls to assert_xpath
will use Hpricot expressions. This method does not depend on invoke_hpricot
, and subsequent test cases will run in their suite’s mode.
Example: %transclude AssertXPathSuite#test_assert_hpricot
See also: assert_hpricot
400 401 402 403 404 405 406 407 |
# File 'lib/assert_xpath.rb', line 400 def assert_hpricot(*args, &block) xml = args.shift || @xdoc || @response.body ## ERGO why @xdoc?? # ERGO document that callseq! require 'hpricot' @xdoc = Hpricot(xml.to_s) # ERGO take that to_s out of all callers return assert_xpath(*args, &block) if args.length > 0 return @xdoc end |
#assert_libxml(*args, &block) ⇒ Object
ERGO more documentation!
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'lib/assert_xpath.rb', line 340 def assert_libxml(*args, &block) xml = args.shift || @xdoc || @response.body xhtml = xml.to_s # CONSIDER fix this like at the source?? xhtml.gsub!('<![CDATA[>', '') xhtml.gsub!('<![CDATA[', '') xhtml.gsub!('//]]]]>', '') xhtml.gsub!(']]>', '') if xhtml !~ /^\<\!DOCTYPE\b/ and xhtml !~ /\<\?xml\b/ xhtml = _doc_type[@_favorite_flavor || :html] + "\n" + xhtml if _doc_type[@_favorite_flavor] end # ERGO document we pass HTML level into invoker if xhtml.index('<?xml version="1" standalone="yes"?>') == 0 xhtml.gsub!('<?xml version="1" standalone="yes"?>', '') xhtml.strip! # ERGO what is libxml's problem with that line??? end # # FIXME blog that libxml will fully validate your ass... xp = xhtml =~ /\<\!DOCTYPE/ ? XML::HTMLParser.new() : XML::Parser.new() xhtml = '<xml/>' unless xhtml.any? xp.string = xhtml # FIXME blog we don't work with libxml-ruby 3.8.4 # XML::Parser.default_load_external_dtd = false XML::Parser.default_pedantic_parser = false # FIXME optionalize that #what? xp doc = xp.parse #what? doc #puts doc.debug_dump @xdoc = doc.root # @xdoc.namespace ||= XML::NS.new('') #pp (@xdoc.root.public_methods - public_methods).sort return assert_xpath(*args, &block) if args.length > 0 return @xdoc end |
#assert_rexml(*args, &block) ⇒ Object
Processes a string of text, or the hidden @response.body
, using REXML, and sets the hidden @xdoc
node. Does not depend on, or change, the values of invoke_hpricot
, invoke_libxml
, or invoke_rexml
Example: %transclude AssertXPathSuite#test_assert_rexml
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/assert_xpath.rb', line 307 def assert_rexml(*args, &block) contents = (args.shift || @response.body).to_s # ERGO benchmark these things contents.gsub!('\\\'', ''') contents.gsub!('//<![CDATA[<![CDATA[', '') contents.gsub!('//<![CDATA[', '') contents.gsub!('//]]>', '') contents.gsub!('//]>', '') contents.gsub!('//]]', '') contents.gsub!('//]', '') begin @xdoc = REXML::Document.new(contents) rescue REXML::ParseException => e raise e unless e. =~ /attempted adding second root element to document/ @xdoc = REXML::Document.new("<xhtml>#{ contents }</xhtml>") end _bequeath_attributes(@xdoc) assert_xpath(*args, &block) if args != [] return (assert_xpath('/*') rescue nil) if @xdoc end |
#assert_tag_id(tag, id = {}, diagnostic = nil, diagnostic2 = nil, &block) ⇒ Object
Wraps the common idiom assert_xpath('descendant-or-self::./my_tag[ @id = "my_id" ]')
. Depends on assert_xml
-
tag
- an XML node name, such asdiv
orinput
. If this is a:symbol
, we prefix “.//
” -
id
- string, symbol, or hash identifying the node. ids must not contain punctuation -
diagnostic
- optional string to add to failure message -
block|node|
- optional block containing assertions, based onassert_xpath
, which operate on this node as the XPath ‘.’ current node.
Returns the obtained REXML::Element node
Examples:
assert_tag_id '/span/div', "audience_#{ring.id}" do
assert_xpath 'table/tr/td[1]' do |td|
#...
assert_tag_id :form, :for_sale
end
end
%transclude AssertXPathSuite#test_assert_tag_id_and_tidy
%transclude AssertXPathSuite#test_assert_tag_id
597 598 599 600 601 |
# File 'lib/assert_xpath.rb', line 597 def assert_tag_id(tag, id = {}, diagnostic = nil, diagnostic2 = nil, &block) # if id is not a hash, diagnostic might be a hash too! # CONSIDER upgrade assert_tag_id to use each_element_with_attribute assert_xpath build_xpath(tag, id, diagnostic), diagnostic2 || diagnostic, &block end |
#assert_tidy(messy = @response.body, verbosity = :noisy) ⇒ Object
%html <a name=‘assert_tidy’></a> Thin wrapper on the Tidy command line program (the one released 2005 September)
-
messy
- optional string containing messy HTML. Defaults to@response.body
. -
verbosity
- optional noise level. Defaults to:noisy
, which reports most errors. :verbose reports all information, and other value will repress all of Tidy’s screams of horror regarding the quality of your HTML.
The resulting XHTML loads into assert_xml
. Use this to retrofit assert_xpath
tests to less-than-pristine HTML.
assert_tidy
obeys invoke_rexml
and invoke_hpricot
, to select its HTML parser
Examples:
get :adjust, :id => transaction.id # <-- fetches ill-formed HTML
assert_tidy @response.body, :quiet # <-- upgrades it to well-formed
assert_tag_id '//table', :payment_history do # <-- sees good XML
#...
end
%transclude AssertXPathSuite#test_assert_tag_id_and_tidy
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 |
# File 'lib/assert_xpath.rb', line 674 def assert_tidy(messy = @response.body, verbosity = :noisy) scratch_html = RAILS_ROOT + '/tmp/scratch.html' # CONSIDER a railsoid tmp file system? # CONSIDER yield to something to respond to errors? File.open(scratch_html, 'w'){|f| f.write(messy) } gripes = `tidy -eq #{scratch_html} 2>&1` gripes.split("\n") # TODO kvetch about client_input_channel_req: channel 0 rtype [email protected] reply 1 puts gripes if verbosity == :verbose puts gripes.reject{|g| g =~ / - Info\: / or g =~ /Warning\: missing \<\!DOCTYPE\> declaration/ or g =~ /proprietary attribute/ or g =~ /lacks "(summary|alt)" attribute/ } if verbosity == :noisy assert_xml `tidy -wrap 1001 -asxhtml #{ scratch_html } 2>/dev/null` # CONSIDER that should report serious HTML deformities end |
#assert_xml(*args, &block) ⇒ Object
%html <a name=‘assert_xml’></a>
Prepare XML for assert_xpath et al
-
xml
- optional string containing XML. Without it, we read@response.body
-
xpath, diagnostic, block
- optional arguments passed toassert_xpath
Sets and returns the new secret @xdoc
REXML::Element root call-seq:
assert_xml(xml = @response.body <em>[, assert_xpath arguments]</em>) -> @xdoc, or assert_xpath's return value
Assertions based on assert_xpath
will call this automatically if the secret @xdoc
is nil
. This implies we may freely call assert_xpath
after any method that populates @response.body
– if @xdoc
is nil
. When in doubt, call assert_xml
explicitly
assert_xml
also translates the contents of assert_select
nodes. Use this to bridge assertions from one system to another. For example:
Returns the first node in the XML
Examples:
assert_select 'div#home_page' do |home_page|
assert_xml home_page # <-- calls home_page.to_s
assert_xpath ".//img[ @src = '#{newb.image_uri(self)}' ]"
deny_tag_id :form, :edit_user
end
%transclude AssertXPathSuite#test_assert_long_sick_expression See: AssertXPathSuite#test_assert_xml_drill
294 295 296 297 |
# File 'lib/assert_xpath.rb', line 294 def assert_xml(*args, &block) using :libxml? # prop-ulates @helper return @helper.assert_xml(self, *args, &block) end |
#assert_xpath(*args, &block) ⇒ Object
%html <a name=‘assert_xpath’></a>
Return the first XML node matching a query string. Depends on assert_xml
to populate our secret internal REXML::Element, @xdoc
-
xpath
- a query string describing a path among XML nodes. See: XPath Tutorial Roundup -
diagnostic
- optional string to add to failure message -
block|node|
- optional block containing assertions, based onassert_xpath
, which operate on this node as the XPath ‘.’ currentnode
Returns the obtained REXML::Element node
Examples:
render :partial => 'my_partial'
assert_xpath '/table' do |table|
assert_xpath './/p[ @class = "brown_text" ]/a' do |a|
assert_equal user.login, a.text # <-- native <code>REXML::Element#text</code> method
assert_match /\/my_name$/, a[:href] # <-- attribute generated by +assert_xpath+
end
assert_equal "ring_#{ring.id}", table.id! # <-- attribute generated by +assert_xpath+, escaped with !
end
%transclude AssertXPathSuite#test_assert_xpath
See: AssertXPathSuite#test_indent_xml, XPath Checker
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
# File 'lib/assert_xpath.rb', line 440 def assert_xpath(*args, &block) return assert_tag_id(*args, &block) if args.length > 1 and args[1].kind_of?(Symbol) or args[1].kind_of?(Hash) # return assert_any_xpath(xpath, diagnostic) { # block.call(@xdoc) if block # true # } xpath, diagnostic = args stash_xdoc do xpath = symbol_to_xpath(xpath) node = @xdoc.search(xpath).first @xdoc = node || flunk_xpath(diagnostic, "should find xpath <#{_esc xpath}>") @xdoc = _bequeath_attributes(@xdoc) block.call(@xdoc) if block # ERGO tribute here? return @xdoc end end |
#deny_any_xpath(xpath, matcher, diagnostic = nil) ⇒ Object
Negates assert_any_xpath
. Depends on assert_xml
-
xpath
- a query string describing a path among XML nodes. This must succeed - usedeny_xpath
for simple queries that must fail -
matcher
- optional Regular Expression to test node contents. Ifxpath
locates multiple nodes, this pattern must fail to match each node to pass the assertion. -
diagnostic
- optional string to add to failure message
Contrived example:
assert_xml '<heathrow><terminal>5</terminal><lean>methods</lean></heathrow>'
Test::Unit::AssertionFailedError,
/all xpath.*\.\/\/lean.*not have.*methods/ do
deny_any_xpath :lean, /methods/
end
deny_any_xpath :lean, /denver/
See: AssertXPathSuite#test_deny_any_xpath, assert_raise (on Ruby) - Don’t Just Say “No”
558 559 560 561 562 563 564 565 566 567 568 569 570 571 |
# File 'lib/assert_xpath.rb', line 558 def deny_any_xpath(xpath, matcher, diagnostic = nil) @xdoc or assert_xml xpath = symbol_to_xpath(xpath) assert_any_xpath xpath, nil, diagnostic do |node| if node.inner_text =~ matcher flunk_xpath( diagnostic, "all xpath <#{_esc xpath}> nodes should not have pattern <?>", matcher ) end end end |
#deny_tag_id(tag, id, diagnostic = nil, diagnostic2 = nil) ⇒ Object
Negates assert_tag_id
. Depends on assert_xml
Example - see: assert_xml
See: assert_tag_id
609 610 611 |
# File 'lib/assert_xpath.rb', line 609 def deny_tag_id(tag, id, diagnostic = nil, diagnostic2 = nil) deny_xpath build_xpath(tag, id, diagnostic), diagnostic2 || diagnostic end |
#deny_xpath(*args) ⇒ Object
Negates assert_xpath
. Depends on assert_xml
Examples:
assert_tag_id :td, :object_list do
assert_xpath "table[ position() = 1 and @id = 'object_#{object1.id}' ]"
deny_xpath "table[ position() = 2 and @id = 'object_#{object2.id}' ]"
end # find object1 is still displayed, but object2 is not in position 2
%transclude AssertXPathSuite#test_deny_xpath
469 470 471 472 473 474 475 476 477 |
# File 'lib/assert_xpath.rb', line 469 def deny_xpath(*args) return deny_tag_id(*args) if args.length > 1 and args[1].kind_of?(Symbol) or args[1].kind_of?(Hash) xpath, diagnostic = args @xdoc or assert_xml xpath = symbol_to_xpath(xpath) @xdoc.search(xpath).first and flunk_xpath(diagnostic, "should not find: <#{_esc xpath}>") end |
#drill(&block) ⇒ Object
ERGO document me
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/assert_xpath.rb', line 127 def drill(&block) if block # ERGO harmonize with bang! version # ERGO deal if the key ain't a valid variable unless tribute(block) # ERGO pass in self (node)? sib = self nil while (sib = sib.next_sibling) and sib.node_type != :element p sib # ERGO do tests ever get here? q = sib and _bequeath_attributes(sib).drill(&block) return sib if q raise Test::Unit::AssertionFailedError.new("can't find beyond <#{xpath}>") end end return self # ERGO if block returns false/nil, find siblings until it passes. # throw a test failure if it don't. # ERGO axis concept end |
#indent_xml(doc = @xdoc || assert_xml) ⇒ Object
Pretty-print a REXML::Element or Hpricot::Elem
-
doc
- optional element. Defaults to the currentassert_xml
document
returns: string with indented XML
Use this while developing a test case, to see what the current @xdoc
node contains (as populated by assert_xml
and manipulated by assert_xpath
et al)
For example:
assert_javascript 'if(x == 42) answer_great_question();'
assert_js_if /x.*42/ do
puts indent_xml # <-- temporary statement to see what to assert next!
end
See: AssertXPathSuite#test_indent_xml
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File 'lib/assert_xpath.rb', line 630 def indent_xml(doc = @xdoc || assert_xml) if doc.kind_of?(Hpricot::Elem) or doc.kind_of?(Hpricot::Doc) zdoc = doc doc = REXML::Document.new(doc.to_s.strip) rescue nil unless doc # Hpricot didn't well-formify the HTML! return zdoc.to_s # note: not indented, but good enough for error messages end end # require 'rexml/formatters/default' # bar = REXML::Formatters::Pretty.new # out = String.new # bar.write(doc, out) # return out return doc.to_s # ERGO reconcile with 1.8.6.111! x = StringIO.new doc.write(x, 2) return x.string # CONSIDER does REXML have a simpler way? end |
#invoke_hpricot ⇒ Object
Subsequent assert_xml
calls will use Hpricot. (Alternately, assert_hpricot
will run one assertion in Hpricot mode.) Put invoke_hpricot
into setup
() method, to run entire suites in this mode. These test cases explore some differences between the two assertion systems: %transclude AssertXPathSuite#test_assert_long_xpath
225 226 227 228 |
# File 'lib/assert_xpath.rb', line 225 def invoke_hpricot @xdoc = nil @helper = HpricotHelper.new end |
#invoke_libxml(favorite_flavor = :html) ⇒ Object
Subsequent assert_xml
calls will use LibXML. (Alternately, assert_libxml
will run one assertion in Hpricot mode.) Put invoke_libxml
into setup
() method, to run entire suites in this mode.
237 238 239 240 241 |
# File 'lib/assert_xpath.rb', line 237 def invoke_libxml(favorite_flavor = :html) @_favorite_flavor = favorite_flavor @xdoc = nil @helper = LibxmlHelper.new end |
#invoke_rexml ⇒ Object
Subsequent assert_xml
calls will use REXML. See invoke_hpricot
to learn the various differences between the two systems
260 261 262 263 |
# File 'lib/assert_xpath.rb', line 260 def invoke_rexml @xdoc = nil @helper = RexmlHelper.new end |
#using(kode) ⇒ Object
380 381 382 383 |
# File 'lib/assert_xpath.rb', line 380 def using(kode) @helper ||= RexmlHelper.new # ERGO escallate this! return @helper.send(kode) end |
#validate_as(type) ⇒ Object
Temporarily sets the validation type to :xml, :html, or :xhtml
333 334 335 336 337 338 |
# File 'lib/assert_xpath.rb', line 333 def validate_as(type) # FIXME use or lose this @_favorite_flavor, formerly = type, @_favorite_flavor yield ensure @_favorite_flavor = type end |