Here's how the DSL evolved, along with the implementation.. I think I'll want to migrate some of my straight regular expressions over to the nested scans that ryanb introduced me to in another thread... So far I only used it once place, but I do find it leads to clearer code.
The one gotcha that was holding me up for a while here was forgetting to manually populate hidden form paramters into the issued request, but resolving that wasn't too bad... Now to extract out the DSL to a common base class for all my acceptance tests to use....
If the DSL is useful to anyone else, please feel free to take it and use it however you like. I haven't had to deal with file uploads or select boxes yet, but it does handle email, link clicking, and form submitting. Its a little weak on its checkbox/radio button support as it doesn't know how to find the value="foo" from the displayed text.... I don't know of any user interface/accessibility guidelines for knowing how to link the two... I make something work using labels, but I haven't seen label tags used that way in real life....
require "#{File.dirname(__FILE__)}/../test_helper"class AccountCreationTest < ActionController::IntegrationTest
def setup
@emails = ActionMailer::Base.deliveries
@emails.clear
end
def test_personal_account_creation
email="test@example.net"
password="T3st!ng"
username="TestUser"
goto_home_page
click_link "Create Account"
fill_out_form do |f|
f.type username, :label=>"Username"
f.type email, :label=>"E-mail Address"
f.type password, :label=>"Password"
f.type password, :label=>"Password Confirmation"
f.check "Personal", :label=>"Account Type"
f.click "Next Step"
end
fill_out_form do |f|
f.type "Test", :label=>"First Name"
f.type "User", :label=>"Last Name"
f.click "Create Account"
end
find_email("Account Pending Confirmation", email) do |e|
e.contains username
get e.link("/account/activate")
end
fill_out_form do |f|
f.type username, :label=>"Username"
f.type password, :label=>"Password"
f.click "Activate Account"
end
verify_link_present "Logout"
end
private
def verify_link_present(text)
assert response.body.include?(text), "Link with \"#{text}\" not present"
end
def fill_out_form
form = FormSurrogate.new response
yield form
form.add_hidden
post_via_redirect form.url, form.params
end
def find_email(subject, to)
email = @emails.find { |e| (e.subject==subject) && (e.to.include?(to))}
assert_not_nil email, "No matching email found"
email = EmailSurrogate.new(email)
yield email
end
def goto_home_page
get "/"
assert_response :success
assert_template "register/list"
end
def click_link(link_text)
destination = get_destination_for_link(link_text)
assert_select "a[href*=#{destination}]", link_text
get destination
assert_response :success
end
def get_destination_for_link(link_text)
regexp = /<a(.*?)href="(.*?)"(.*?)>#{link_text}<\/a>/
md = regexp.match(response.body)
md[2]
end
end
class EmailSurrogate
include Test::Unit::Assertions
def initialize(email)
@email=email
end
def contains(text)
assert_match /#{text}/, email.body
end
def link(text)
regexp = /(https?:\/\/\S*?#{text}\S*)\s/m
md = regexp.match(email.body)
md[1]
end
def email
@email
end
end
class FormSurrogate
include Test::Unit::Assertions
def initialize(response)
@response=response
@params={}
@url=""
end
def add_hidden
response.body.scan(/<form.*?action="#{url}".*?>.*?<\/form>/m) do |form|
form.scan(/<input[^>]+?type="hidden".*?>/) do |hidden_element|
name = hidden_element.scan(/\bname="(.*?)"/)
value = hidden_element.scan(/\bvalue="(.*?)"/)
add_to_params(name.first, value.first) unless name.empty? || value.empty?
end
end
end
def type(text, label)
add_to_params(get_name_from_label(label[:label]),text)
end
def check(value, label)
add_to_params get_name_from_label(label[:label]), value
end
def click(button_text)
@url = get_destination_for_button(button_text)
end
def get_destination_for_button(button_text)
regexp = /<form(.*?)action="(.*?)"(.*?)>(.*?)<input(.*?)type="submit"(.*?)value="#{button_text}"(.*?)>/m
md = regexp.match(response.body)
assert false, "No button found with button text:\"#{button_text}\"." if md.nil?
md[2]
end
def get_name_from_label(label)
regexp = /<label(.*?)for="(.*?)"(.*?)>#{label}<\/label>/
md = regexp.match(response.body)
assert false, "No label found with for=\"#{label}\"." if md.nil?
regexp = /<input(.*?)id="#{md[2]}(.*?)"(.*?)name="(.*?)"(.*?) \/>/
md = regexp.match(response.body)
assert false, "No input found with linked to visual label:\"#{label}\"." if md.nil?
md[4]
end
def add_to_params(name,value)
hash=@params
while name.include?("[") do
key, name = extract_key_from_name(name)
hash[key]= {} unless hash.has_key? key
hash = hash[key]
end
hash[name]=value
end
def extract_key_from_name(name)
regexp = /(.*)\[(.*)\]/
md = regexp.match(name)
return md[1], md[2]
end
def response
@response
end
def params
@params
end
def url
@url
end
end
My RoR journey -- thoughts on learning RoR and lessons learned in applying TDD and agile practices.