jakewendt-html_test 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +105 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/jakewendt-html_test.gemspec +68 -0
- data/lib/DTD/xhtml-lat1.ent +196 -0
- data/lib/DTD/xhtml-special.ent +80 -0
- data/lib/DTD/xhtml-symbol.ent +237 -0
- data/lib/DTD/xhtml.soc +14 -0
- data/lib/DTD/xhtml1-frameset.dtd +1235 -0
- data/lib/DTD/xhtml1-strict.dtd +978 -0
- data/lib/DTD/xhtml1-transitional.dtd +1201 -0
- data/lib/DTD/xhtml1.dcl +192 -0
- data/lib/assertions.rb +53 -0
- data/lib/html_test.rb +96 -0
- data/lib/jakewendt-html_test.rb +1 -0
- data/lib/link_validator.rb +175 -0
- data/lib/url_checker.rb +142 -0
- data/lib/url_selector.rb +57 -0
- data/lib/validate_filter.rb +55 -0
- data/lib/validator.rb +121 -0
- data/rails/init.rb +1 -0
- data/script/validate +47 -0
- data/test/controller_test.rb +120 -0
- data/test/integration_test.rb +73 -0
- data/test/invalid.html +15 -0
- data/test/link_validator_test.rb +141 -0
- data/test/public/image.jpg +0 -0
- data/test/rhtml_template.rhtml +1 -0
- data/test/rxml_template.rxml +2 -0
- data/test/test_helper.rb +61 -0
- data/test/untidy.html +19 -0
- data/test/valid.html +14 -0
- data/test/valid_links.html +22 -0
- data/test/validate_all_test.rb +16 -0
- metadata +101 -0
data/lib/url_selector.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Html
|
2
|
+
module Test
|
3
|
+
module UrlSelector
|
4
|
+
def skip_url?(url, root_url = nil)
|
5
|
+
if url.blank? || unsupported_protocol?(url) || special_url?(url)
|
6
|
+
true
|
7
|
+
else
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def special_url?(url)
|
13
|
+
[/^javascript:/, /^mailto:/, /^\#$/].any? { |pattern| url =~ pattern }
|
14
|
+
end
|
15
|
+
|
16
|
+
def external_http?(url, root_url = nil)
|
17
|
+
if root_url
|
18
|
+
http_protocol?(url) && !url.starts_with?(root_url)
|
19
|
+
else
|
20
|
+
http_protocol?(url)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def http_protocol?(url)
|
25
|
+
url =~ %r{^http(?:s)?://} ? true : false
|
26
|
+
end
|
27
|
+
|
28
|
+
def has_protocol?(url)
|
29
|
+
url =~ %r{^[a-z]+://} ? true : false
|
30
|
+
end
|
31
|
+
|
32
|
+
def unsupported_protocol?(url)
|
33
|
+
has_protocol?(url) && !http_protocol?(url)
|
34
|
+
end
|
35
|
+
|
36
|
+
def anchor_urls
|
37
|
+
select("a").map { |l| l.attributes['href'] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def image_urls
|
41
|
+
select("img").map { |i| i.attributes['src'] }
|
42
|
+
end
|
43
|
+
|
44
|
+
def form_urls
|
45
|
+
select("form").map { |i| i.attributes['action'] }
|
46
|
+
end
|
47
|
+
|
48
|
+
def response_body
|
49
|
+
self.respond_to?(:response) ? response.body : @response.body
|
50
|
+
end
|
51
|
+
|
52
|
+
def select(pattern)
|
53
|
+
HTML::Selector.new(pattern).select(HTML::Document.new(response_body).root)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Html
|
2
|
+
module Test
|
3
|
+
class ValidateFilter
|
4
|
+
attr_accessor :request, :response, :params, :validators
|
5
|
+
|
6
|
+
include ::Test::Unit::Assertions
|
7
|
+
include ::Html::Test::Assertions
|
8
|
+
|
9
|
+
def initialize(controller)
|
10
|
+
self.request = controller.request
|
11
|
+
self.response = controller.response
|
12
|
+
self.params = controller.params
|
13
|
+
self.validators = controller.class.validators
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_page
|
17
|
+
url = request.request_uri
|
18
|
+
return if (!should_validate? || ValidateFilter.already_validated?(url))
|
19
|
+
# assert_validates(validators, response.body.strip, url, :verbose => true)
|
20
|
+
assert_validates(validators, response.body.strip, url )
|
21
|
+
ValidateFilter.mark_url_validated(url)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.already_validated?(url)
|
25
|
+
if Html::Test::Validator.revalidate_all
|
26
|
+
false
|
27
|
+
else
|
28
|
+
validated_urls[url]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.mark_url_validated(url)
|
33
|
+
validated_urls[url] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.validated_urls
|
37
|
+
@validated_urls ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# Override this method if you only want to validate a subset of pages
|
41
|
+
def should_validate?
|
42
|
+
response.status =~ /200/ &&
|
43
|
+
(response.headers['Content-Type'] =~ /text\/html/i || response.body =~ /<html/)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Used in testing (of html_test_extension plugin)
|
47
|
+
# to remove the validated_urls hash
|
48
|
+
# so can test with the same url.
|
49
|
+
def self.clear_validated_urls
|
50
|
+
@validated_urls = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/validator.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'net/http'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Html
|
6
|
+
module Test
|
7
|
+
class Validator
|
8
|
+
|
9
|
+
# verbose = true shows "validating ..."
|
10
|
+
# verbose = false shows NOTHING
|
11
|
+
@@verbose = true
|
12
|
+
cattr_accessor :verbose
|
13
|
+
#
|
14
|
+
# revalidate_all = true will validate every call to a url
|
15
|
+
# revalidate_all = false will only validate the first call to a url
|
16
|
+
@@revalidate_all = true
|
17
|
+
cattr_accessor :revalidate_all
|
18
|
+
|
19
|
+
@@tidy_ignore_list = []
|
20
|
+
cattr_accessor :tidy_ignore_list
|
21
|
+
|
22
|
+
# For local validation you might change this to http://localhost/validator/htdocs/check
|
23
|
+
DEFAULT_W3C_URL = "http://validator.w3.org/check"
|
24
|
+
@@w3c_url = DEFAULT_W3C_URL
|
25
|
+
cattr_accessor :w3c_url
|
26
|
+
|
27
|
+
# Whether the W3C validator should show the HTML document being validated in
|
28
|
+
# its response. Set to 0 to disable.
|
29
|
+
@@w3c_show_source = "1"
|
30
|
+
cattr_accessor :w3c_show_source
|
31
|
+
|
32
|
+
DEFAULT_DTD = File.join(File.dirname(__FILE__), 'DTD', 'xhtml1-strict.dtd')
|
33
|
+
|
34
|
+
# Path to DTD file that the xmllint validator uses
|
35
|
+
def self.dtd(document)
|
36
|
+
DEFAULT_DTD
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate an HTML document string using tidy.
|
40
|
+
# Code excerpted from the rails_tidy plugin
|
41
|
+
def self.tidy_errors(body)
|
42
|
+
tidy = RailsTidy.tidy_factory
|
43
|
+
tidy.clean(body)
|
44
|
+
errors = tidy.errors.empty? ? nil :
|
45
|
+
tidy.errors.delete_if { |e| tidy_ignore_list.select { |p| e =~ p }.size > 0 }.join("\n")
|
46
|
+
tidy.release
|
47
|
+
errors.blank? ? nil : errors
|
48
|
+
end
|
49
|
+
|
50
|
+
# Validate an HTML document string by going to the online W3C validator.
|
51
|
+
# Credit for the original code goes to Scott Baron (htonl)
|
52
|
+
def self.w3c_errors(body)
|
53
|
+
response = Net::HTTP.post_form(URI.parse(w3c_url),
|
54
|
+
{'ss'=>w3c_show_source, 'fragment'=>body})
|
55
|
+
status = response['x-w3c-validator-status']
|
56
|
+
if status != 'Valid'
|
57
|
+
# Reference in the stylesheets
|
58
|
+
response.body.sub!(%r{@import "./base.css"}, %Q{@import "#{File.dirname(w3c_url)}/base.css"})
|
59
|
+
response_file = find_unique_path(File.join(tmp_dir, "w3c_response.html"))
|
60
|
+
open(response_file, "w") { |f| f.puts(response.body) }
|
61
|
+
"W3C status #{status}. Response from W3C was written to the file #{response_file}"
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Validate an HTML document string using the xmllint command line validator tool.
|
68
|
+
# Returns nil if validation passes and an error message otherwise.
|
69
|
+
# Original code taken from the book "Enterprise Integration with Ruby"
|
70
|
+
def self.xmllint_errors(body)
|
71
|
+
error_file = create_tmp_file("xmllint_error")
|
72
|
+
doc_file = command = nil
|
73
|
+
if dtd(body) =~ /^doctype$/i
|
74
|
+
# Use the DOCTYPE declaration
|
75
|
+
doc_file = create_tmp_file("xmllint", body)
|
76
|
+
command = "xmllint --noout --valid #{doc_file} &> #{error_file}"
|
77
|
+
else
|
78
|
+
# Override the DOCTYPE declaration
|
79
|
+
doc_file = create_tmp_file("xmllint", body.sub(/<!DOCTYPE[^>]+>/m, ""))
|
80
|
+
command = "xmllint --noout --dtdvalid #{dtd(body)} #{doc_file} &> #{error_file}"
|
81
|
+
end
|
82
|
+
system(command)
|
83
|
+
status = $?.exitstatus
|
84
|
+
if status == 0
|
85
|
+
return nil
|
86
|
+
else
|
87
|
+
failure_doc = File.join(tmp_dir, "xmllint_last_response.html")
|
88
|
+
FileUtils.cp doc_file, failure_doc
|
89
|
+
return ("command='#{command}'. HTML document at '#{failure_doc}'. " +
|
90
|
+
IO.read(error_file))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def self.find_unique_path(path)
|
96
|
+
filename = File.basename(path)
|
97
|
+
ext = File.extname(filename)
|
98
|
+
size_no_ext = filename.size - ext.size
|
99
|
+
filename_no_ext = filename[0, size_no_ext]
|
100
|
+
counter = 2
|
101
|
+
while File.exists?(path)
|
102
|
+
new_filename = [filename_no_ext, "-", counter, ext].join
|
103
|
+
path = File.join(File.dirname(path), new_filename)
|
104
|
+
counter += 1
|
105
|
+
end
|
106
|
+
path
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.create_tmp_file(name, contents = "")
|
110
|
+
tmp_file = Tempfile.new(name)
|
111
|
+
tmp_file.puts(contents)
|
112
|
+
tmp_file.close
|
113
|
+
tmp_file.path
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.tmp_dir
|
117
|
+
Dir::tmpdir
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'jakewendt-html_test' if RAILS_ENV == 'test'
|
data/script/validate
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# == Synopsis
|
3
|
+
# This script validates an HTML page via HTTP and checks for broken
|
4
|
+
# links and images. Links are followed one step away from the start page.
|
5
|
+
# HTML validation is also done for pages linked internally from the start page
|
6
|
+
# (i.e. relative URLs).
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
# vendor/plugins/html_test/script/validate http://my.url.com [options]
|
10
|
+
#
|
11
|
+
# Options:
|
12
|
+
#
|
13
|
+
# --no-follow:: Don't follow any anchor or images URLs on
|
14
|
+
# the start page (i.e. only one page is requested).
|
15
|
+
#
|
16
|
+
# --validators validator_list:: A comma separated list of validators to use
|
17
|
+
# for HTML validation. Supported validators:
|
18
|
+
# tidy, xmllint, w3c. Default validator is
|
19
|
+
# tidy if it is installed, otherwise w3c.
|
20
|
+
#
|
21
|
+
# --dtd dtd_path:: Path to the DTD file to use for xmllint
|
22
|
+
# validation. By default xmllint will use
|
23
|
+
# the XHTML 1.0 strict DTD. Set dtd_path
|
24
|
+
# to "doctype" to make xmllint use the
|
25
|
+
# DTD specified in the DOCTYPE tag
|
26
|
+
# (can be slow).
|
27
|
+
#
|
28
|
+
# --skip skip_patterns:: A comma separated list of regexp patterns for
|
29
|
+
# URLs to not visit. Using the pattern '.*'
|
30
|
+
# is equivalent to the --no-follow option.
|
31
|
+
#
|
32
|
+
# --only only_pattern:: Only visit URLs matching given regexp pattern
|
33
|
+
#
|
34
|
+
# --no-external:: Do not visit external URLs, i.e. URLs with
|
35
|
+
# different domain than the start page.
|
36
|
+
#
|
37
|
+
# --quiet:: Don't output anything unless there is a failure.
|
38
|
+
|
39
|
+
require 'optparse'
|
40
|
+
require 'rdoc/usage'
|
41
|
+
require 'uri'
|
42
|
+
|
43
|
+
require File.join(File.dirname(__FILE__), "..", "..", "..", "..", "test", "test_helper")
|
44
|
+
require File.join(File.dirname(__FILE__), "..", "lib", "html_test")
|
45
|
+
|
46
|
+
options = Html::Test::LinkValidator.parse_command_line(ARGV) rescue RDoc::usage
|
47
|
+
Html::Test::LinkValidator.new(ARGV[0], options)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "..", "..", "test", "test_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
3
|
+
|
4
|
+
ActionController::Routing::Routes.draw do |map|
|
5
|
+
map.connect 'test/:action', :controller => 'test'
|
6
|
+
end
|
7
|
+
|
8
|
+
class Html::Test::ControllerTest < ActionController::TestCase
|
9
|
+
def setup
|
10
|
+
@controller = TestController.new
|
11
|
+
@request = ActionController::TestRequest.new
|
12
|
+
@response = ActionController::TestResponse.new
|
13
|
+
|
14
|
+
ActionController::Base.validate_all = false
|
15
|
+
ActionController::Base.check_urls = true
|
16
|
+
ActionController::Base.check_redirects = true
|
17
|
+
Html::Test::Validator.tidy_ignore_list = []
|
18
|
+
Html::Test::UrlChecker.any_instance.stubs(:rails_public_path).returns(File.join(File.dirname(__FILE__), "public"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_assert_validates_success
|
22
|
+
get :valid
|
23
|
+
assert_response :success
|
24
|
+
assert_validates # Should validate tidy, w3c, and xmllint
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_assert_tidy_failure
|
28
|
+
get :untidy
|
29
|
+
assert_response :success
|
30
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
31
|
+
assert_tidy
|
32
|
+
end
|
33
|
+
assert_w3c
|
34
|
+
assert_xmllint
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_assert_w3c_failure
|
38
|
+
get :invalid
|
39
|
+
assert_response :success
|
40
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
41
|
+
assert_w3c
|
42
|
+
end
|
43
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
44
|
+
assert_xmllint
|
45
|
+
end
|
46
|
+
assert_tidy
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_url_no_route
|
50
|
+
assert_raise(Html::Test::InvalidUrl) do
|
51
|
+
get :url_no_route
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_url_no_action
|
56
|
+
assert_raise(Html::Test::InvalidUrl) do
|
57
|
+
get :url_no_action
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: figure out why those tests don't work
|
62
|
+
# def test_url_rhtml_template_exists
|
63
|
+
# get :rhtml_template
|
64
|
+
# end
|
65
|
+
|
66
|
+
# def test_url_rxml_template_exists
|
67
|
+
# get :rxml_template
|
68
|
+
# end
|
69
|
+
|
70
|
+
def test_url_action_no_template
|
71
|
+
get :action_no_template
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_redirect_no_action
|
75
|
+
assert_raise(Html::Test::InvalidUrl) do
|
76
|
+
get :redirect_no_action
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_redirect_valid_action
|
81
|
+
get :redirect_valid_action
|
82
|
+
assert_response :redirect
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_redirect_external
|
86
|
+
get :redirect_external
|
87
|
+
assert_response :redirect
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_redirect_valid_with_host
|
91
|
+
get :redirect_valid_with_host
|
92
|
+
assert_response :redirect
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_image_file_exists
|
96
|
+
get :image_file_exists
|
97
|
+
assert_response :success
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_image_does_not_exist
|
101
|
+
assert_raise(Html::Test::InvalidUrl) do
|
102
|
+
get :image_file_does_not_exist
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_urls_to_resolve
|
107
|
+
checker = Html::Test::UrlChecker.new(@controller)
|
108
|
+
checker.stubs(:response_body).returns(<<-HTML
|
109
|
+
<a href="anchor_url">hej</a>
|
110
|
+
Some text and <div>markup</div>
|
111
|
+
<img src="image_url"/>
|
112
|
+
Some more text
|
113
|
+
<form action="form_url">
|
114
|
+
Some text
|
115
|
+
</form>
|
116
|
+
HTML
|
117
|
+
)
|
118
|
+
assert_equal(%w(anchor_url form_url image_url), checker.send(:urls_to_check).sort)
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "..", "..", "test", "test_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
3
|
+
|
4
|
+
ActionController::Routing::Routes.draw do |map|
|
5
|
+
map.connect 'test/:action', :controller => 'test'
|
6
|
+
end
|
7
|
+
ApplicationController.validate_all = false
|
8
|
+
ApplicationController.check_urls = true
|
9
|
+
ApplicationController.check_redirects = true
|
10
|
+
|
11
|
+
class Html::Test::IntegrationTest < ActionController::IntegrationTest
|
12
|
+
def test_assert_valides_invokes_all
|
13
|
+
get('/test/valid')
|
14
|
+
assert_response :success
|
15
|
+
|
16
|
+
[:tidy_errors, :w3c_errors, :xmllint_errors].each do |method|
|
17
|
+
Html::Test::Validator.expects(method).with(@response.body)
|
18
|
+
end
|
19
|
+
assert_validates
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_assert_tidy_invoked
|
23
|
+
get('/test/valid')
|
24
|
+
assert_response :success
|
25
|
+
Html::Test::Validator.expects(:tidy_errors).with(@response.body)
|
26
|
+
assert_tidy
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_assert_valid_success
|
30
|
+
get('/test/valid')
|
31
|
+
assert_response :success
|
32
|
+
assert_validates
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_assert_tidy_failure
|
36
|
+
file_string = TestController.test_file_string(:untidy)
|
37
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
38
|
+
assert_tidy(file_string)
|
39
|
+
end
|
40
|
+
assert_w3c(file_string)
|
41
|
+
assert_xmllint(file_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_assert_w3c_failure
|
45
|
+
file_string = TestController.test_file_string(:invalid)
|
46
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
47
|
+
assert_w3c(file_string)
|
48
|
+
end
|
49
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
50
|
+
assert_xmllint(file_string)
|
51
|
+
end
|
52
|
+
assert_raise(Test::Unit::AssertionFailedError) do
|
53
|
+
Html::Test::Validator.expects(:dtd).returns("doctype")
|
54
|
+
assert_xmllint(file_string)
|
55
|
+
end
|
56
|
+
assert_tidy(file_string)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_url_no_route
|
60
|
+
TestController.any_instance.expects(:rescue_action).with() { |e| e.class == Html::Test::InvalidUrl }
|
61
|
+
get '/test/url_no_route'
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_url_no_route_relative
|
65
|
+
TestController.any_instance.expects(:rescue_action).with() { |e| e.class == Html::Test::InvalidUrl }
|
66
|
+
get '/test/url_no_route_relative'
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_redirect_valid_action
|
70
|
+
get '/test/redirect_valid_action'
|
71
|
+
assert_response :redirect
|
72
|
+
end
|
73
|
+
end
|
data/test/invalid.html
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
6
|
+
<title>A perfectly invalid document</title>
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
<h1>This is a perfectly invalid XHTML 1 document</h1>
|
10
|
+
|
11
|
+
Here are some unquoted chars: <>
|
12
|
+
|
13
|
+
Tidy may not complain about this document, but that's just crazy.
|
14
|
+
</body>
|
15
|
+
</html>
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "..", "..", "..", "..", "test", "test_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "test_helper")
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
# Stub out the HTTP requests
|
6
|
+
module Net
|
7
|
+
class HTTP
|
8
|
+
@@test_requests = []
|
9
|
+
cattr_accessor :test_requests
|
10
|
+
|
11
|
+
@@test_with_links = false
|
12
|
+
cattr_accessor :test_with_links
|
13
|
+
|
14
|
+
def self.get_response(uri)
|
15
|
+
self.test_requests << uri.to_s
|
16
|
+
|
17
|
+
if uri.path =~ /valid_links/ or test_with_links
|
18
|
+
file = :valid_links
|
19
|
+
else
|
20
|
+
file = :valid
|
21
|
+
end
|
22
|
+
test_with_links = false
|
23
|
+
return OpenStruct.new({
|
24
|
+
:body => TestController.test_file_string(file),
|
25
|
+
:code => "200",
|
26
|
+
:header => {'content-type' => 'text/html'}
|
27
|
+
})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Html::Test::LinkValidatorTest < Test::Unit::TestCase
|
33
|
+
def setup
|
34
|
+
Net::HTTP.test_requests = []
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_parse_command_line
|
38
|
+
# Missing URL
|
39
|
+
assert_raise(RuntimeError) { Html::Test::LinkValidator.parse_command_line(["--no-follow"]) }
|
40
|
+
|
41
|
+
# Missing skip patterns
|
42
|
+
options_no_skip = ["http://my.url.com", "--no-follow", "--validators", "w3c,tidy,xmllint",
|
43
|
+
"--no-external", "--dtd", "some.dtd", "--only", "some/url", "--skip"]
|
44
|
+
assert_raise(OptionParser::MissingArgument) do
|
45
|
+
Html::Test::LinkValidator.parse_command_line(options_no_skip.dup)
|
46
|
+
end
|
47
|
+
|
48
|
+
# All options - valid
|
49
|
+
assert_equal({
|
50
|
+
:follow_links => false,
|
51
|
+
:validators => ["w3c", "tidy", "xmllint"],
|
52
|
+
:follow_external => false,
|
53
|
+
:dtd => "some.dtd",
|
54
|
+
:only_pattern => Regexp.new("some/url"),
|
55
|
+
:skip_patterns => [/pattern1/, /pattern2/]
|
56
|
+
}, Html::Test::LinkValidator.parse_command_line(options_no_skip << "pattern1,pattern2"))
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_default_options
|
60
|
+
url = "http://localhost:3000"
|
61
|
+
validator = Html::Test::LinkValidator.new(url)
|
62
|
+
|
63
|
+
assert_equal url, Net::HTTP.test_requests.first
|
64
|
+
assert_equal 1, Net::HTTP.test_requests.size
|
65
|
+
|
66
|
+
assert validator.options[:follow_links]
|
67
|
+
assert_equal ['tidy'], validator.options[:validators]
|
68
|
+
assert_equal [], validator.options[:skip_patterns]
|
69
|
+
assert_nil validator.options[:only_pattern]
|
70
|
+
assert validator.options[:follow_external]
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_quiet
|
74
|
+
validator = Html::Test::LinkValidator.new("http://my.cool.site", {:quiet => true})
|
75
|
+
# Even in quiet mode the log should be populated
|
76
|
+
assert_match /my\.cool\.site/, validator.log
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_link_follow_page
|
80
|
+
validator = Html::Test::LinkValidator.new("http://site.com/dir/valid_links")
|
81
|
+
# All links visited
|
82
|
+
assert_equal(["http://site.com/dir/valid_links",
|
83
|
+
"http://site.com/link1",
|
84
|
+
"http://site.com/dir/link2",
|
85
|
+
"http://site.com/dir/foobar/link3",
|
86
|
+
"http://foobar.com/external"].sort,
|
87
|
+
Net::HTTP.test_requests.sort)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_link_follow_root
|
91
|
+
%w(http://site.com http://site.com/).each do |url|
|
92
|
+
Net::HTTP.test_requests = []
|
93
|
+
Net::HTTP.test_with_links = true
|
94
|
+
validator = Html::Test::LinkValidator.new(url)
|
95
|
+
# All links visited
|
96
|
+
assert_equal([url,
|
97
|
+
"http://site.com/link1",
|
98
|
+
"http://site.com/link2",
|
99
|
+
"http://site.com/foobar/link3",
|
100
|
+
"http://foobar.com/external"].sort,
|
101
|
+
Net::HTTP.test_requests.sort)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_link_follow_dir
|
106
|
+
Net::HTTP.test_with_links = true
|
107
|
+
validator = Html::Test::LinkValidator.new("http://site.com/dir/")
|
108
|
+
# All links visited
|
109
|
+
assert_equal(["http://site.com/dir/",
|
110
|
+
"http://site.com/link1",
|
111
|
+
"http://site.com/dir/link2",
|
112
|
+
"http://site.com/dir/foobar/link3",
|
113
|
+
"http://foobar.com/external"].sort,
|
114
|
+
Net::HTTP.test_requests.sort)
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_skip_patterns
|
118
|
+
url = "http://my.cool.site/valid_links"
|
119
|
+
validator = Html::Test::LinkValidator.new(url,
|
120
|
+
{:skip_patterns => [/link[12]/, /http/]})
|
121
|
+
assert_equal [url, "http://my.cool.site/foobar/link3"].sort,
|
122
|
+
Net::HTTP.test_requests.sort
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_only_pattern
|
126
|
+
url = "http://my.cool.site/dir/valid_links"
|
127
|
+
validator = Html::Test::LinkValidator.new(url, {:only_pattern => /link[12]/})
|
128
|
+
assert_equal [url, "http://my.cool.site/link1", "http://my.cool.site/dir/link2"].sort,
|
129
|
+
Net::HTTP.test_requests.sort
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_follow_external_false
|
133
|
+
Net::HTTP.test_with_links = true
|
134
|
+
validator = Html::Test::LinkValidator.new("http://site.com/dir/", {:follow_external => false})
|
135
|
+
assert_equal(["http://site.com/dir/",
|
136
|
+
"http://site.com/link1",
|
137
|
+
"http://site.com/dir/link2",
|
138
|
+
"http://site.com/dir/foobar/link3"].sort,
|
139
|
+
Net::HTTP.test_requests.sort)
|
140
|
+
end
|
141
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Some contents
|