Created
February 23, 2011 14:43
-
-
Save ericbeland/840498 to your computer and use it in GitHub Desktop.
RSpec Matcher to Check All Links are Valid in a Rails View
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Minor update of the code from this link to make it work with Rails 3: | |
# http://feefighters.com/devblog/2008/11/07/testing-for-broken-links-with-rspec/ | |
class HaveValidLinks | |
# Pass in :fragment=>true if the response contains only an HTML fragment | |
def initialize(options={}) | |
@fragment = options[:fragment] | |
end | |
# This is the matching method that is called by RSpec | |
# The response is passed in as an argument, and we extract the body from it | |
def matches?(response) | |
@response = response | |
fragment = response.body | |
fragment = wrap_with_xhtml_header(fragment) if @fragment | |
@broken_links = [] | |
root = HTML::Document.new(fragment).root | |
selector = HTML::Selector.new('a') | |
matches = selector.select(root) | |
matches.each do |m| | |
verify_url(m.attributes['href']) | |
end | |
selector = HTML::Selector.new('form') | |
matches = selector.select(root) | |
matches.each do |m| | |
verify_url(m.attributes['action']) | |
end | |
return true if @broken_links.empty? | |
@message = @broken_links.collect {|href| "Couldn't find route for: #{href}"}.join("\n") | |
return false | |
end | |
def description | |
"have valid links" | |
end | |
def failure_message | |
" expected links to be valid, but we found some broken links:\n #{@message}" | |
end | |
def negative_failure_message | |
" expected links to not be valid, but there were no invalid routes!" | |
end | |
private | |
# Here we do the heavy lifting to check if a given url is a valid route | |
# An ActionController::TestRequest is instantiated, | |
# and we call ActionController::Routing::Routes.recognize to test the route | |
def verify_url(href) | |
if not href.nil? | |
href.sub!(/https?:\/\/testomatix.com\//, '/') | |
href.sub!(/https?:\/\/test.host\//, '/') | |
href.sub!(/https?:\/\/localhost[:0-9]+\//, '/') | |
if not ignore_route?(href) | |
# Assume given controller | |
request = ActionController::TestRequest.new() | |
# request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method | |
request.path = href | |
begin | |
Rails.application.routes.recognize_path(request) | |
rescue ActionController::RoutingError | |
if href.match(/^https?:\/\/transfs.com/) or href.match(/^https?:\/\/test.host/) | |
@broken_links.push href | |
end | |
end | |
end | |
end | |
end | |
# This method takes an HTML fragment as a param, | |
# and returns an HTML doc that wraps this fragment | |
def wrap_with_xhtml_header(fragment) | |
ret = <<-EOS | |
<?xml version="1.0" encoding="UTF-8"?> | |
<!DOCTYPE html PUBLIC | |
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" | |
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> | |
<head> | |
<title>Test</title> | |
</head> | |
<body> | |
#{fragment} | |
</body> | |
</html> | |
EOS | |
ret | |
end | |
# Any routes that we want to ignore go here | |
def ignored_routes | |
[ /^mailto:/, /^\/blog/ ] | |
end | |
# utility method for testing urls against the list of ignored route matchers | |
def ignore_route?(href) | |
ignored_routes.each do |reg| | |
return true if Regexp.new(reg).match(href) | |
end | |
return false | |
end | |
end | |
# These are RSpec helper methods that make it easy to use HaveValidLinks in your specs | |
def have_valid_links | |
HaveValidLinks.new | |
end | |
def have_valid_links_fragment | |
HaveValidLinks.new(:fragment=>true) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment