Permalink
Browse files

add tests, make into gem, add assert_all_valid_markup

  • Loading branch information...
1 parent 7359c5f commit 6361de70b69d42ee17dfa5d9a951027976dd80d1 Matt Conway committed Oct 28, 2009
Showing with 425 additions and 43 deletions.
  1. +5 −0 .document
  2. +8 −0 .gitignore
  3. +20 −0 LICENSE
  4. +71 −0 README.rdoc
  5. +55 −10 Rakefile
  6. +2 −0 VERSION
  7. +1 −1 init.rb
  8. +73 −32 lib/assert_valid_markup.rb
  9. +1 −0 rails/init.rb
  10. +178 −0 test/assert_valid_markup_test.rb
  11. +11 −0 test/test_helper.rb
View
@@ -0,0 +1,5 @@
+README.rdoc
+lib/**/*.rb
+bin/*
+features/**/*.feature
+LICENSE
View
@@ -0,0 +1,8 @@
+*.sw?
+.DS_Store
+coverage
+rdoc
+pkg
+.idea
+*.gemspec
+*.gem
View
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Matt Conway
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -0,0 +1,71 @@
+= assert_valid_markup plugin for Rails
+
+
+Validate your (X)HTML from within your functional tests. If anything changes to invalidate
+your markup, you'll know pronto.
+
+If xmllint/xmlcatalog are available in your PATH, this plugin will use them to validate your xml.
+When doing so, it will automatically fetch and cache all DTDs locally so that you don't incur a
+network hit on future runs.
+
+Otherwise, validations will be done using the W3C Validator web service (http://validator.w3.org/).
+Responses from the web service are cached, so your tests aren't slowed down unless something has changed.
+
+== Installation
+
+Install as a gem:
+
+ gem install assert_valid_markup
+
+and edit config/environments/test.rb:
+
+ config.gem 'assert_valid_markup'
+
+or install as a plugin:
+
+ ./script/plugin install http://github.com/wr0ngway/assert_valid_markup
+
+
+== Usage
+
+Calling the assertion with no parameters validates whatever is in @request.body,
+which is automatically set by the existing get/post/etc helpers. For example:
+
+ class FooControllerTest < Test::Unit::TestCase
+ def test_bar_markup
+ get :bar
+ assert_valid_markup
+ end
+ end
+
+Add a string parameter to the assertion to validate any random fragment. Por ejemplo:
+
+ class FooControllerTest < Test::Unit::TestCase
+ def test_bar_markup
+ assert_valid_markup "<div>Hello, world.</div>"
+ end
+ end
+
+For the ultimate in convenience, use the class-level methods to validate a slew of
+actions in one line. Par exemple:
+
+ class FooControllerTest < Test::Unit::TestCase
+ assert_valid_markup :bar, :baz, :qux
+ end
+
+ # automatically validate all GETs in BarControllerTest
+ class BarControllerTest < Test::Unit::TestCase
+ assert_all_valid_markup
+ end
+
+ # Add this to test_helper to automatically validate the responses for ALL GETs in ALL controllers
+ class ActionController::TestCase < ActiveSupport::TestCase
+ assert_all_valid_markup
+ end
+
+
+== Credits
+
+Scott Raymond <sco@scottraymond.net>. Released under the MIT license.
+Matt Conway <matt@conwaysplace.com> (xmllint additions)
+Latest version: http://github.com/wr0ngway/assert_valid_markup
View
@@ -1,16 +1,61 @@
-task :default => [:test]
+require 'rubygems'
+require 'rake'
-PKG_NAME = "assert_valid_markup"
-PKG_VERSION = "0.2"
+begin
+ require 'jeweler'
+ Jeweler::Tasks.new do |gem|
+ gem.name = "assert_valid_markup"
+ gem.summary = %Q{Fork of assert_valid_markup, sped up by using xmllint locally rather than hitting w3c service}
+ gem.description = %Q{Also contains some conveniences such as a flag to clear w3c cache at runtime, and some other cleanups}
+ gem.email = "wr0ngway@yahoo.com"
+ gem.homepage = "http://github.com/wr0ngway/assert_valid_markup"
+ gem.authors = ["Matt Conway"]
+ gem.add_development_dependency "mocha"
+ gem.add_development_dependency "shoulda"
+ gem.add_development_dependency "activesupport"
+ gem.add_dependency "xml-simple"
+ gem.files = FileList["[A-Z][A-Z]*", "init.rb", "{lib,rails}/**/*"]
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
+ end
+ Jeweler::GemcutterTasks.new
+rescue LoadError
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
+end
-task :docs do
- sh "rdoc lib"
+require 'rake/testtask'
+Rake::TestTask.new(:test) do |test|
+ test.libs << 'lib' << 'test'
+ test.pattern = 'test/**/*_test.rb'
+ test.verbose = true
end
-task :package => :docs do
- sh "tar czf #{PKG_NAME}-#{PKG_VERSION}.tar.gz *"
+begin
+ require 'rcov/rcovtask'
+ Rcov::RcovTask.new do |test|
+ test.libs << 'test'
+ test.pattern = 'test/**/*_test.rb'
+ test.verbose = true
+ end
+rescue LoadError
+ task :rcov do
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
+ end
end
-#task :test do
-# sh "cd test; ruby assert_valid_markup_test.rb"
-#end
+task :test => :check_dependencies
+
+task :default => :test
+
+require 'rake/rdoctask'
+Rake::RDocTask.new do |rdoc|
+ if File.exist?('VERSION')
+ version = File.read('VERSION')
+ else
+ version = ""
+ end
+
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = "assert_valid_markup #{version}"
+ rdoc.rdoc_files.include('README*')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
View
@@ -0,0 +1,2 @@
+0.5.0
+
View
@@ -1 +1 @@
-require 'assert_valid_markup' if ENV["RAILS_ENV"] == "test"
+require File.dirname(__FILE__) + "/rails/init"
View
@@ -2,13 +2,19 @@
require 'net/http'
require 'digest/md5'
require 'open-uri'
-require 'FileUtils'
+require 'fileutils'
+require 'tempfile'
+require 'xmlsimple'
+require 'cgi'
class Test::Unit::TestCase
-
- @@catalog_path = File.expand_path("~/.xml-catalogs")
- @@use_local_validation = system("xmllint --version > /dev/null 2>&1")
-
+
+ @@default_avm_options = {
+ :catalog_path => File.expand_path("~/.xml-catalogs"),
+ :validation_service => system("xmllint --version > /dev/null 2>&1") ? :local : :w3c,
+ :dtd_validate => true
+ }
+
# Assert that markup (html/xhtml) is valid according the W3C validator web service.
# By default, it validates the contents of @response.body, which is set after calling
# one of the get/post/etc helper methods. You can also pass it a string to be validated.
@@ -22,27 +28,15 @@ class Test::Unit::TestCase
# assert_valid_markup
# end
#
- def assert_valid_markup(fragment=@response.body, dtd_validate=true)
- if @@use_local_validation
- result = local_validate(fragment, dtd_validate)
- assert result.empty?, result.collect {|l| l.gsub(/^[^:]*:/, "Invalid markup: line ")}.join("\n")
+ def assert_valid_markup(fragment=@response.body, options={})
+ opts = @@default_avm_options.merge(options)
+ result = ''
+ if opts[:validation_service] == :local
+ result = local_validate(fragment, opts[:dtd_validate], opts[:catalog_path])
else
- begin
- filename = File.join Dir::tmpdir, 'markup.' + Digest::MD5.hexdigest(fragment).to_s
- begin
- response = File.open filename do |f| Marshal.load(f) end
- rescue
- response = Net::HTTP.start('validator.w3.org').post2('/check', "fragment=#{CGI.escape(fragment)}&output=xml")
- File.open filename, 'w+' do |f| Marshal.dump response, f end
- end
- markup_is_valid = response['x-w3c-validator-status']=='Valid'
- message = markup_is_valid ? '' : XmlSimple.xml_in(response.body)['messages'][0]['msg'].collect{ |m| "Invalid markup: line #{m['line']}: #{CGI.unescapeHTML(m['content'])}" }.join("\n")
- assert markup_is_valid, message
- rescue SocketError
- # if we can't reach the validator service, just let the test pass
- assert true
- end
+ result = w3c_validate(fragment, opts[:dtd_validate])
end
+ assert result.empty?, result
end
# Class-level method to quickly create validation tests for a bunch of actions at once.
@@ -64,11 +58,36 @@ def self.assert_valid_markup(*actions)
end
end
- def local_validate(xmldata, dtd_validate=true)
- catalog_file = "#{@@catalog_path}/catalog"
- if ! File.exists? @@catalog_path
- puts "Creating xml catalog at: #{@@catalog_path}"
- FileUtils.mkdir_p(@@catalog_path)
+ # Class-level method to to turn on validation for the response from any successful html request via "get"
+ def self.assert_all_valid_markup
+ self.class_eval do
+ # automatically check markup for all successfull GETs
+ def get_with_assert_valid_markup(*args)
+ get_without_assert_valid_markup(*args)
+ assert_valid_markup if ! @@skip_validation && @request.format.html? && @response.success?
+ end
+ alias_method_chain :get, :assert_valid_markup
+ end
+ end
+
+ @@skip_validation = false
+
+ # Allows one to skip validation for the given block - useful when you use assert_all_valid_markup and need to only
+ # skip validation for a handful of tests
+ def skip_markup_validation
+ begin
+ @@skip_validation = true
+ yield
+ ensure
+ @@skip_validation = false
+ end
+ end
+
+ def local_validate(xmldata, dtd_validate, catalog_path)
+ catalog_file = "#{catalog_path}/catalog"
+ if ! File.exists? catalog_path
+ puts "Creating xml catalog at: #{catalog_path}"
+ FileUtils.mkdir_p(catalog_path)
out = `xmlcatalog --noout --create '#{catalog_file}' 2>&1`
if $? != 0
puts out
@@ -89,7 +108,7 @@ def local_validate(xmldata, dtd_validate=true)
if match = line.match(/Resolve: pubID (.*) sysID (.*)/)
pubid = match[1]
sysid = match[2]
- localdtd = "#{@@catalog_path}/#{sysid.split('/').last}"
+ localdtd = "#{catalog_path}/#{sysid.split('/').last}"
if ! File.exists? localdtd
puts "Adding xml catalog resource\n\tpublic id: '#{pubid}'\n\turi: '#{sysid}'\n\tfile: '#{localdtd}'"
open(localdtd, "w") {|f| f.write(open(sysid).read())}
@@ -102,7 +121,29 @@ def local_validate(xmldata, dtd_validate=true)
end
end
validation_failed = validation_output.grep(/^#{Regexp.escape(tmpfile.path)}:/)
- return validation_failed
+ return validation_failed.collect {|l| l.gsub(/^[^:]*:/, "Invalid markup: line ")}.join("\n")
+ end
+
+ def w3c_validate(fragment, dtd_validate)
+ validation_result = ''
+ begin
+ filename = File.join Dir::tmpdir, 'markup.' + Digest::MD5.hexdigest(fragment).to_s
+ if ! ENV['NO_CACHE_VALIDATION']
+ response = File.open filename {|f| Marshal.load(f) } unless ENV['NO_CACHE_VALIDATION'] rescue nil
+ end
+ if ! response
+ response = Net::HTTP.start('validator.w3.org').post2('/check', "fragment=#{CGI.escape(fragment)}&output=xml")
+ File.open filename, 'w+' do |f| Marshal.dump response, f end
+ end
+ markup_is_valid = response['x-w3c-validator-status']=='Valid'
+ if ! markup_is_valid
+ doc = XmlSimple.xml_in(response.body)
+ validation_result = doc['messages'][0]['msg'].collect{ |m| "Invalid markup: line #{m['line']}: #{CGI.unescapeHTML(m['content'])}" }.join("\n")
+ end
+ rescue SocketError
+ # if we can't reach the validator service, just let the test pass
+ puts "WARNING: Could not reach w3c validator service"
+ end
+ return validation_result
end
-
end
View
@@ -0,0 +1 @@
+require 'assert_valid_markup'
Oops, something went wrong.

0 comments on commit 6361de7

Please sign in to comment.