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, &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(xpath, diagnostic = nil, &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) ⇒ Object
Negates
assert_tag_id
. -
#deny_xpath(xpath, diagnostic = nil) ⇒ 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
485 486 487 488 489 490 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 |
# File 'lib/assert_xpath.rb', line 485 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
398 399 400 401 402 403 404 405 |
# File 'lib/assert_xpath.rb', line 398 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!
338 339 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 |
# File 'lib/assert_xpath.rb', line 338 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
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/assert_xpath.rb', line 305 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, &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 or symbol uniquely identifying the node. This 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
591 592 593 594 |
# File 'lib/assert_xpath.rb', line 591 def assert_tag_id(tag, id, diagnostic = nil, &block) # CONSIDER upgrade assert_tag_id to use each_element_with_attribute assert_xpath build_xpath(tag, id), 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
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
# File 'lib/assert_xpath.rb', line 667 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
292 293 294 295 |
# File 'lib/assert_xpath.rb', line 292 def assert_xml(*args, &block) using :libxml? # prop-ulates @helper return @helper.assert_xml(self, *args, &block) end |
#assert_xpath(xpath, diagnostic = nil, &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
438 439 440 441 442 443 444 445 446 447 448 449 450 451 |
# File 'lib/assert_xpath.rb', line 438 def assert_xpath(xpath, diagnostic = nil, &block) # return assert_any_xpath(xpath, diagnostic) { # block.call(@xdoc) if block # true # } 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”
552 553 554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/assert_xpath.rb', line 552 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) ⇒ Object
Negates assert_tag_id
. Depends on assert_xml
Example - see: assert_xml
See: assert_tag_id
602 603 604 |
# File 'lib/assert_xpath.rb', line 602 def deny_tag_id(tag, id, diagnostic = nil) deny_xpath build_xpath(tag, id), diagnostic end |
#deny_xpath(xpath, diagnostic = nil) ⇒ 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
465 466 467 468 469 470 471 |
# File 'lib/assert_xpath.rb', line 465 def deny_xpath(xpath, diagnostic = nil) @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
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/assert_xpath.rb', line 125 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
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 |
# File 'lib/assert_xpath.rb', line 623 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
223 224 225 226 |
# File 'lib/assert_xpath.rb', line 223 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.
235 236 237 238 239 |
# File 'lib/assert_xpath.rb', line 235 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
258 259 260 261 |
# File 'lib/assert_xpath.rb', line 258 def invoke_rexml @xdoc = nil @helper = RexmlHelper.new end |
#using(kode) ⇒ Object
378 379 380 381 |
# File 'lib/assert_xpath.rb', line 378 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
331 332 333 334 335 336 |
# File 'lib/assert_xpath.rb', line 331 def validate_as(type) # FIXME use or lose this @_favorite_flavor, formerly = type, @_favorite_flavor yield ensure @_favorite_flavor = type end |