-
Notifications
You must be signed in to change notification settings - Fork 3
github clone of http://assert2.rubyforge.org/svn/
githubsvnclone/assert2
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
The assert{ 2.0 } project asks a simple question: How the leanest possible assertions can yield the maximum diagnostics? Its latest assertion, assert_xhtml, answers this question for HTML. To begin, grab it with: gem install nokogiri assert2 ==require 'assert2/xhtml'== All assert{ 2.0 } dependencies are optional. If you have Nokogiri (>=1.2.2), you can test Rails views like this: user = users(:Moses) get :edit_user, :id => user.id assert_xhtml do form :action => '/users' do fieldset do legend 'Personal Information' label 'First name' input :type => 'text', :name => 'user[first_name]' :value => user.first_name end end end That's a Rails functional test on a form. The assertion expects the form to target the given action, and contain a fieldset, a legend, a label, and a populated text input field. The assertion forgives any other details, such as intervening structural tags, excess spaces, or extra attributes; and complains if any required detail is missing, out of order, or ill-formed. The DSL inside that block is Nokogiri::HTML::Builder notation. Generally speaking, anything Nokogiri can build, you can specify. ===arguments=== Call assert_xhtml(my_xml){} to interrogate your XML. When called without an argument, the method reads @response.body. ===without!=== Every assert* has a matching deny* method. assert_xhtml recognizes the special element without! as a request to fail if the given elements do indeed appear in your output: get :info, :record_id => record.id assert_xhtml do div :class => :content do without!{ div :class => :download } end end That assertion will fail if the outer <div class='content'> tag does not exist, or if any inner <div class='download'> does exist. The without! element respects your document layout. This assertion passes... assert_xhtml SAMPLE_LIST do ul{ li{ ul{ li 'Sales report' without!{ li 'All Sales report criteria' } } } } end ...even though the target document contains an <li>All Sales report criteria</li>: <ul style='font-size: 18'> <li>model <ul> <li>Billings report</li> <li>Sales report</li> <li>Billings criteria</li> <li>Common system</li> </ul> </li> <li>controller <ul> <li>All Sales report criteria</li> <li>All Billings reports</li> </ul> </li> </ul> The two <li> elements appear in different <ul> lists, so the assertion does not associate them. The committee does not yet know what without!{ without!{} } does, so please do not rely on its current behavior, whatever that is! ===escapes=== Certain elements, such as <select> and <id>, have the same names as internal methods. If you experience a bizarre error message, such as "wrong argument type Hash (expected Array)", add a ! to the end of the element, like this: assert_xhtml do h2 'Sites' select! :id => 'sites', :name => 'sites[]', :multiple => :multiple, :size => SaleController::LIST_SIZE end ===text=== An element such as h3{ 'text' } will match <h3> text </h3>, with leading and trailing blanks, but it won't match <h3><span>text</span></h3>. This rule prevents runaway matches between high- and low-level elements. A text specification may be a /regexp/, and an element may contain both text and attributes: h2 /Sites/, :style => /right/ The next section illustrates the text() directive. ===:xpath!=>=== assert_xhtml works by throwing away structural information. If your document structure is more unruly and wild, it might need more constraints. Use an :xpath! attribute to apply raw XPath specifications to your target elements. This assertion detect the rather pedestrian fact that your <title> element remains inside your <html><head> block - and it did not escape and rampage off to somewhere else: assert_xhtml do title :xpath! => 'parent::head/parent::html' do text 'Chamber of Commerce - Info - Hope Orphanage' end end (That code also shows the 'text' directive, inserting text contents directly into the enclosing element. Sometimes the directive is the most convenient way to specify the content.) An :xpath! of a number evaluates to the 1-based index of an item in its parent. This assertion forces list items to appear in the correct order: assert_xhtml do ul :style => 'font-size: 18' do li 'model' do li(:xpath! => 1){ text 'Sales report' } li(:xpath! => 2){ text 'Billings report' } li(:xpath! => 3){ text 'Billings criteria' } end end end ===:verbose! => true=== Sometimes when an assertion fails, you can't tell why. To see each context the assertion considers, add :verbose! => true to the lowest element you know works, and run the tests: assert_xhtml SAMPLE_FORM do fieldset do li :verbose! => true do label 'First name', :for => :user_first_name end end end The verbose option works as "spew", not as a diagnostic, and it reports each considered element's contents. Because XPath evaluates the <label>, in our example, before the <li>, you might need to comment the <label> out to see a successful spew on the <li>. ===scope=== assert_xhtml{} yields its block to Nokogiri::HTML::Builder, which turns every method call into an HTML element. This freedom comes at a price - you can't easily call your own methods! Use this scope trick to pass your outer scope into the specification: get :edit_user, :id => users(:Moses).id scope = self assert_xhtml do form :action => '/users' do input :value => scope.users(:Moses).first_name end end Notice we could improve that test by declaring a variable, user = users(:Moses), in the outer scope, and simply passing the user variable itself into the specification. ===:class=>=== The :class attribute is magic. This assertion passes... assert_xhtml SAMPLE_LIST do ul :class => :kalika do li 'Billings report' end end ...despite the actual HTML contains <ul class='kalika goddess'>. This feature simulates the CSS Selector notation that matches classes by their cascading effects. ===class & ID shortcuts=== Nokogiri expands div.rad.thing! to <div class='rad' id='thing'/>. That means you don't need to write div :class => 'rad', :id => 'thing' (or ul :class => :kalika). You can then put other arguments after the shortcut, and the <div> in our example receives them, too. ===diagnostic message=== When this assertion fails, it prints out... - your reference elements, rendered as HTML - the best "near-miss" region of your sample HTML Note that the diagnostic displays without! and xpath! tags literally. If your assertion fails, and the diagnostic does not display a relevant near-miss, the best recourse is to wrap your target element with a known container element. Don't do this: assert_xhtml do li 'my one lonely list item' end Do this: assert_xhtml do ul.my_unordered_list! do li 'my one lonely list item' end end That is guaranteed to match the first <ul id='my_unordered_list'> in your document. ===RSpec=== The matching "specification", in RSpec language, is be_html_with{}. Its syntax and behavior are the same: it 'should have a cute form' do render '/users/new' response.body.should be_html_with{ form :action => '/users' do fieldset do legend 'Personal Information' label 'First nome' input :type => 'text', :name => 'user[first_name]' end end } end Good hunting!
Releases
No releases published
Packages 0
No packages published