RSpecsters:
I like nested XPath expressions, because the outer XPath assertion can clip the
diagnostic of the inner XPath assertion. You don't get the whole page spewed
into your face when you fault!
This specification, for example, tests Yury Kotlyarov's user login page, from:
http://github.com/yura/howto-rspec-custom-matchers/tree/master
it 'should have xpathic tags' do
render '/users/new'
response.body.should be_xml_with{
xpath :form, :action => '/users' do
xpath :fieldset do
xpath :'legend[ contains(., "Personal Information") ]' and
xpath :'label[ contains(., "First name") ]' and
xpath :input, :type => 'text', :name => 'user[first_name]'
end
end
}
end
That tests this (otherwise innocuous) new.html.erb:
<form action="/users">
<fieldset>
<legend>Personal Information</legend>
<ol>
<li id="control_user_first_name">
<label for="user_first_name">First name</label>
<input type="text" name="user[first_name]" id="user_first_name" />
</li>
</ol>
</fieldset>
</form>
If that code had any major complex <%= erb %> activity, the tests^W
specifications would keep it honest.
Let's change the specification, to simulate a bug, and try it:
xpath :input, :type => 'text', :name => 'user[first_nome]' # was _name
That provides this mind-blast of errata:
'/users/new should have xpathic tags' FAILED
xpath: "descendant-or-self::inp...@type = $type and @name = $name]"
arguments: {"name"=>"user[first_nome]", "type"=>"text"}
xml context:
<fieldset>
<legend>
Personal Information
</legend>
<ol>
<li id='control_user_first_name'>
<label for='user_first_name'>
First name
</label>
<input name='user[first_name]' type='text' id='user_first_name'/>
</li>
</ol>
</fieldset>
assert{ ( ( xpath(:"legend[ contains(., \"Personal Information\") ]") ) and
( ( ( xpath(:"label[ contains(., \"First name\") ]") ) and
( xpath(:input, { :type => "text", :name => "user[first_nome]" }) ) ) ) ) }
--> nil - should pass
xpath(:"legend[ contains(., \"Personal Information\") ]")
--> <legend> ... </>
xpath(:"label[ contains(., \"First name\") ]")
--> <label for='user_first_name'> ... </>
xpath(:input, { :type => "text", :name => "user[first_nome]" })
--> nil
./spec/views/users/new.html.erb_spec.rb:63:
script/spec:5:
Finished in 0.116823 seconds
2 examples, 1 failure
Note that the error message restricted itself to the XHTML inside the <form>
tag. This is a major benefit when diagnosing a huge page that failed. (But also
note that your HTML, like your code, should come in small reusable snippets,
such as partials, and that these should get tested directly!)
Soon I will upgrade this system to use Nokogiri instead of (>cough<) REXML.
Now, while I go and put the "simple" matcher that does this onto Twitter,
YouTube, Mingle, Facebook, Mindfuck, Reddit, Tumblog, LinkedIn, and Gist, you
all can just read it below my sig.
--
Phlip
http://www.zeroplayer.com/
require File.dirname(__FILE__) + "/../../spec_helper"
require 'assert2/xpath'
require 'spec/matchers/wrap_expectation'
Spec::Runner.configure do |c|
c.include Test::Unit::Assertions
end # TODO blog this
describe "/users/new" do
it "should have user form" do
render '/users/new'
response.should have_form('/users') do
with_field_set 'Personal Information' do
with_text_field 'First name', 'user', 'first_name'
end
end
end
class BeXmlWith
def initialize(scope, &block)
@scope, @block = scope, block
end
def matches?(stwing, &block)
waz_xdoc = @xdoc
@scope.wrap_expectation self do
@scope.assert_xhtml stwing
return (block || @block || proc{}).call
end
ensure
@xdoc = waz_xdoc
end
attr_accessor :failure_message
def negative_failure_message
"yack yack yack"
end
end
def be_xml_with(&block)
BeXmlWith.new(self, &block)
end
def be_xml_with_(&block)
waz_xdoc = @xdoc
simple_matcher 'yo' do |given, matcher|
wrap_expectation matcher do
assert_xhtml given # this works
block.call # crashes with a nil.first error!
end
end
ensure
@xdoc = waz_xdoc
end
it 'should have xpathic tags' do
render '/users/new'
response.body.should be_xml_with{
xpath :form, :action => '/users' do
xpath :fieldset do
xpath :'legend[ contains(., "Personal Information") ]' and
xpath :'label[ contains(., "First name") ]' and
xpath :input, :type => 'text', :name => 'user[first_name]'
end
end
}
end
end
_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users