alexdunae-w3c_validators 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,62 @@
1
+ module W3CValidators
2
+ class Results
3
+ attr_reader :uri, :checked_by, :doctype, :css_level, :charset, :validity, :debug_messages
4
+
5
+ def initialize(options = {})
6
+ @messages = []
7
+ @uri = options[:uri]
8
+ @checked_by = options[:checked_by]
9
+ @doctype = options[:doctype]
10
+ @css_level = options[:css_level]
11
+ @charset = options[:charset]
12
+ if options[:validity].kind_of?(String)
13
+ @validity = options[:validity].downcase.strip == 'true'
14
+ else
15
+ @validity = options[:validity]
16
+ end
17
+ @debug_messages = {}
18
+ end
19
+
20
+ def add_message(type, params = {})
21
+ uri = params[:uri] ||= @uri
22
+ @messages << Message.new(uri, type, params)
23
+ end
24
+
25
+ def add_error(params = {})
26
+ add_message(:error, params)
27
+ end
28
+
29
+ def add_warning(params = {})
30
+ add_message(:warning, params)
31
+ end
32
+
33
+ def add_debug_message(key, val)
34
+ @debug_messages[key] = val
35
+ end
36
+
37
+ # Returns either the +DOCTYPE+ or CSS level, whichever is present.
38
+ def checked_against
39
+ return @doctype if @doctype
40
+ return @css_level if @css_level
41
+ nil
42
+ end
43
+
44
+ def is_valid?
45
+ @validity
46
+ end
47
+
48
+ # Returns an array of Message objects.
49
+ def errors
50
+ errors = []
51
+ @messages.each { |msg| errors << msg if msg.is_error? }
52
+ errors
53
+ end
54
+
55
+ # Returns an array of Message objects.
56
+ def warnings
57
+ errors = []
58
+ @messages.each { |msg| errors << msg if msg.is_warning? }
59
+ errors
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,160 @@
1
+ require 'cgi'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'rexml/document'
5
+
6
+ require 'w3c_validators/exceptions'
7
+ require 'w3c_validators/constants'
8
+ require 'w3c_validators/results'
9
+ require 'w3c_validators/message'
10
+
11
+ module W3CValidators
12
+ # Base class for MarkupValidator and FeedValidator.
13
+ class Validator
14
+ VERSION = '0.9.3'
15
+ USER_AGENT = "Ruby W3C Validators/#{Validator::VERSION} (http://code.dunae.ca/w3c_validators/)"
16
+ HEAD_STATUS_HEADER = 'X-W3C-Validator-Status'
17
+ HEAD_ERROR_COUNT_HEADER = 'X-W3C-Validator-Errors'
18
+ SOAP_OUTPUT_PARAM = 'soap12'
19
+
20
+ attr_reader :results, :validator_uri
21
+
22
+ # Create a new instance of the Validator.
23
+ def initialize(options = {})
24
+ @options = options
25
+ end
26
+
27
+ protected
28
+ # Perform a validation request.
29
+ #
30
+ # +request_mode+ must be either <tt>:get</tt>, <tt>:head</tt> or <tt>:post</tt>.
31
+ #
32
+ # Returns Net::HTTPResponse.
33
+ def send_request(options, request_mode = :get)
34
+ response = nil
35
+ results = nil
36
+
37
+ Net::HTTP.start(@validator_uri.host, @validator_uri.port) do |http|
38
+ case request_mode
39
+ when :head
40
+ # perform a HEAD request
41
+ raise ArgumentError, "a URI must be provided for HEAD requests." unless options[:uri]
42
+ query = create_query_string_data(options)
43
+ response = http.request_head(@validator_uri.path + '?' + query)
44
+ when :get
45
+ # send a GET request
46
+ query = create_query_string_data(options)
47
+ response = http.get(@validator_uri.path + '?' + query)
48
+ when :post
49
+ # send a multipart form request
50
+ query, boundary = create_multipart_data(options)
51
+ response = http.post2(@validator_uri.path, query, "Content-type" => "multipart/form-data; boundary=" + boundary)
52
+ else
53
+ raise ArgumentError, "request_mode must be either :get, :head or :post"
54
+ end
55
+ end
56
+
57
+ response.value
58
+ return response
59
+
60
+ rescue Exception => e
61
+ handle_exception e
62
+ end
63
+
64
+ def create_multipart_data(options) # :nodoc:
65
+ boundary = '349832898984244898448024464570528145'
66
+
67
+ # added 2008-03-12: HTML5 validator expects 'file' and 'content' to be the last fields so
68
+ # we process those params separately
69
+ last_params = []
70
+
71
+ # added 2008-03-12: HTML5 validator expects 'file' instead of 'uploaded_file'
72
+ if options[:file] and !options[:uploaded_file]
73
+ options[:uploaded_file] = options[:file]
74
+ end
75
+
76
+ if options[:uploaded_file]
77
+ filename = options[:file_path] ||= 'temp.html'
78
+ content = options[:uploaded_file]
79
+ last_params << "Content-Disposition: form-data; name=\"uploaded_file\"; filename=\"#{filename}\"\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "#{content}\r\n"
80
+ options.delete(:uploaded_file)
81
+ options.delete(:file_path)
82
+ end
83
+
84
+ if options[:content]
85
+ last_params << "Content-Disposition: form-data; name=\"#{CGI::escape('content')}\"\r\n" + "\r\n" + "#{options[:content]}\r\n"
86
+ end
87
+
88
+ misc_params = []
89
+ options.each do |key, value|
90
+ if value
91
+ misc_params << "Content-Disposition: form-data; name=\"#{CGI::escape(key.to_s)}\"\r\n" + "\r\n" + "#{value}\r\n"
92
+ end
93
+ end
94
+
95
+ params = misc_params + last_params
96
+
97
+ multipart_query = params.collect {|p| '--' + boundary + "\r\n" + p}.join('') + "--" + boundary + "--\r\n"
98
+
99
+ [multipart_query, boundary]
100
+ end
101
+
102
+ def create_query_string_data(options) # :nodoc:
103
+ qs = ''
104
+ options.each do |key, value|
105
+ if value
106
+ qs += "#{key}=" + CGI::escape(value.to_s) + "&"
107
+ end
108
+ end
109
+ qs
110
+ end
111
+
112
+ def read_local_file(file_path) # :nodoc:
113
+ fh = File.new(file_path, 'r+')
114
+ src = fh.read
115
+ fh.close
116
+ src
117
+ end
118
+
119
+ private
120
+ #--
121
+ # Big thanks to ara.t.howard and Joel VanderWerf on Ruby-Talk for the exception handling help.
122
+ #++
123
+ def handle_exception(e, msg = '') # :nodoc:
124
+ case e
125
+ when Net::HTTPServerException, SocketError
126
+ msg = "unable to connect to the validator at #{@validator_uri} (response was #{e.message})."
127
+ raise ValidatorUnavailable, msg, caller
128
+ when REXML::ParseException
129
+ msg = "unable to parse the response from the validator."
130
+ raise ParsingError, msg, caller
131
+ else
132
+ raise e
133
+ end
134
+
135
+ if e.respond_to?(:error_handler_before)
136
+ fcall(e, :error_handler_before, self)
137
+ end
138
+
139
+ if e.respond_to?(:error_handler_instead)
140
+ fcall(e, :error_handler_instead, self)
141
+ else
142
+ if e.respond_to? :status
143
+ exit_status(( e.status ))
144
+ end
145
+
146
+ if SystemExit === e
147
+ stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit') ### avoids double message for abort('message')
148
+ end
149
+ end
150
+
151
+ if e.respond_to?(:error_handler_after)
152
+ fcall(e, :error_handler_after, self)
153
+ end
154
+
155
+ exit_status(( exit_failure )) if exit_status == exit_success
156
+ exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
157
+ exit exit_status
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the CSSValidator.
4
+ class CSSValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = CSSValidator.new
8
+
9
+ @invalid_fragment = <<-EOT
10
+ a { color: white; }
11
+ body { margin: blue; }
12
+
13
+ EOT
14
+
15
+ sleep 1
16
+ end
17
+
18
+ def test_overriding_css_profile
19
+ @v.set_profile!(:svgbasic)
20
+ r = @v.validate_text(@invalid_fragment)
21
+ assert_equal 'svgbasic', r.css_level
22
+ end
23
+
24
+ def test_validating_file
25
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_css.css')
26
+ r = @v.validate_file(file_path)
27
+ assert_equal 1, r.errors.length
28
+ end
29
+
30
+ def test_validating_uri
31
+ @v.set_profile!(:svgbasic)
32
+ r = @v.validate_text(@invalid_fragment)
33
+ assert_equal 1, r.errors.length
34
+ end
35
+
36
+ def test_validating_text
37
+ r = @v.validate_text(@invalid_fragment)
38
+ assert_equal 1, r.errors.length
39
+ end
40
+
41
+ def test_validating_text_via_file
42
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_css.css')
43
+ fh = File.new(file_path, 'r+')
44
+ r = @v.validate_file(fh)
45
+ fh.close
46
+ assert_equal 1, r.errors.length
47
+ end
48
+
49
+
50
+
51
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ExceptionTests < Test::Unit::TestCase
4
+ include W3CValidators
5
+
6
+ def setup
7
+ @valid_fragment = <<-EOV
8
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
9
+ <title>Test</title>
10
+ <body>
11
+ <div class="example">This is a test</div>
12
+ </body>
13
+ EOV
14
+ end
15
+
16
+ def test_bad_validator_uri
17
+ ['http://noexist/', 'http://noexist.badtld/',
18
+ 'http://example.com/noexist'].each do |uri|
19
+ v = MarkupValidator.new(:validator_uri => uri)
20
+ assert_raise ValidatorUnavailable do
21
+ r = v.validate_text(@valid_fragment)
22
+ end
23
+ end
24
+ end
25
+
26
+ def test_bad_soap_response
27
+ return # need to set up a test host
28
+ [].each do |uri|
29
+ v = MarkupValidator.new(:validator_uri => uri)
30
+ assert_raise ParsingError do
31
+ r = v.validate_text(@valid_fragment)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,61 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the FeedValidator.
4
+ class FeedValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = FeedValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_validating_uri_with_soap
12
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_feed.xml')
13
+ assert_equal 1, r.errors.length
14
+ assert_equal 1, r.warnings.length
15
+ end
16
+
17
+ def test_validating_file
18
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_feed.xml')
19
+ r = @v.validate_file(file_path)
20
+ assert_equal 1, r.errors.length
21
+ end
22
+
23
+ def test_validating_text
24
+ fragment = <<-EOT
25
+ <?xml version="1.0" encoding="utf-8"?>
26
+ <feed xmlns="http://www.w3.org/2005/Atom">
27
+ <title>Example Feed</title>
28
+ <subtitle>A subtitle.</subtitle>
29
+ <link href="http://example.org/feed/" rel="self"/>
30
+ <link href="http://example.org/"/>
31
+ <updated>2003-12-13T18:30:02Z</updated>
32
+ <author>
33
+ <email>johndoe@example.com</email>
34
+ </author>
35
+ <id>urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6</id>
36
+ <entry>
37
+ <title>Atom-Powered Robots Run Amok</title>
38
+ <link href="http://example.org/2003/12/13/atom03"/>
39
+ <id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
40
+ <updated>2003-12-13T18:30:02Z</updated>
41
+ <summary>Some text.</summary>
42
+ </entry>
43
+ </feed>
44
+ EOT
45
+
46
+ r = @v.validate_text(fragment)
47
+ assert_equal 1, r.errors.length
48
+ end
49
+
50
+
51
+ def test_validating_text_via_file
52
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/invalid_feed.xml')
53
+ fh = File.new(file_path, 'r+')
54
+ r = @v.validate_file(fh)
55
+ fh.close
56
+ assert_equal 1, r.errors.length
57
+ end
58
+
59
+
60
+
61
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
2
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
3
+
4
+ require 'rubygems'
5
+ require 'test/unit'
6
+ require 'w3c_validators'
@@ -0,0 +1,64 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the HTML5Validator.
4
+ class HTML5ValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = NuValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_getting_request_data
12
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/valid_html5.html')
13
+ assert_equal :html5, r.doctype
14
+ assert_equal 'http://code.dunae.ca/w3c_validators/test/valid_html5.html', r.uri
15
+ assert_equal 0, r.errors.length
16
+ assert_equal 0, r.warnings.length
17
+ assert r.is_valid?
18
+ end
19
+
20
+ def test_validating_uri
21
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_html5.html')
22
+ assert_equal 1, r.errors.length
23
+ assert_equal 1, r.warnings.length
24
+ assert !r.is_valid?
25
+ end
26
+
27
+ def test_validating_file
28
+ file = File.dirname(__FILE__) + '/fixtures/invalid_html5.html'
29
+ r = @v.validate_file(file)
30
+ assert_equal 1, r.errors.length
31
+ end
32
+
33
+ def test_validating_text
34
+ valid_fragment = <<-EOV
35
+ <!DOCTYPE html>
36
+ <html lang="en-ca">
37
+ <head>
38
+ <title>HTML 5 Example</title>
39
+ </head>
40
+ <body>
41
+ <!-- should have one error (missing </section>) -->
42
+ <p>This is a sample HTML 5 document.</p>
43
+ <section>
44
+ <h1>Example of paragraphs</h1>
45
+ This is the <em>first</em> paragraph in this example.
46
+ <p>This is the second.</p>
47
+ <p>Test<br>test</p>
48
+ </body>
49
+ </html>
50
+ EOV
51
+
52
+ r = @v.validate_text(valid_fragment)
53
+ assert_equal 1, r.errors.length
54
+ end
55
+
56
+ #def test_validating_text_via_file
57
+ # fh = File.new(File.dirname(__FILE__) + '/fixtures/invalid_html5.html', 'r+')
58
+ # r = @v.validate_file(fh)
59
+ # fh.close
60
+ # assert_equal 1, r.errors.length
61
+ #end
62
+
63
+
64
+ end
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ # Test cases for the MarkupValidator.
4
+ class MarkupValidatorTests < Test::Unit::TestCase
5
+ include W3CValidators
6
+ def setup
7
+ @v = MarkupValidator.new
8
+ sleep 1
9
+ end
10
+
11
+ def test_overriding_doctype
12
+ @v.set_doctype!(:html32, false)
13
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
14
+ assert_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
15
+ end
16
+
17
+ def test_overriding_doctype_for_fallback_only
18
+ @v.set_doctype!(:html32, true)
19
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
20
+ assert_not_equal '-//W3C//DTD HTML 3.2 Final//EN', r.doctype
21
+ end
22
+
23
+ def test_overriding_charset
24
+ @v.set_charset!(:utf_16, false)
25
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
26
+ assert_equal 'utf-16', r.charset
27
+ end
28
+
29
+ def test_overriding_charset_for_fallback_only
30
+ @v.set_doctype!(:utf_16, true)
31
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
32
+ assert_not_equal 'utf-16', r.charset
33
+ end
34
+
35
+ def test_validating_uri_with_head_request
36
+ r = @v.validate_uri_quickly('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
37
+ assert_equal 1, r.errors.length
38
+ assert_equal 0, r.warnings.length
39
+ end
40
+
41
+ def test_validating_uri_with_soap
42
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
43
+ assert_equal 1, r.errors.length
44
+ assert_equal 0, r.warnings.length
45
+ end
46
+
47
+ def test_debugging_uri
48
+ @v.set_debug!
49
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_markup.html')
50
+ assert r.debug_messages.length > 0
51
+ end
52
+
53
+ def test_validating_file
54
+ file = File.dirname(__FILE__) + '/fixtures/invalid_markup.html'
55
+ r = @v.validate_file(file)
56
+ assert_equal 1, r.errors.length
57
+
58
+ assert r.uri =~ /fixtures\/invalid_markup\.html$/
59
+ end
60
+
61
+ def test_validating_text
62
+ valid_fragment = <<-EOV
63
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
64
+ <title>Test</title>
65
+ <body>
66
+ <div class="example">This is a test</div>
67
+ </body>
68
+ EOV
69
+
70
+ r = @v.validate_text(valid_fragment)
71
+ assert_equal 0, r.errors.length
72
+ assert_equal 0, r.warnings.length
73
+ end
74
+
75
+ def test_validating_text_via_file
76
+ fh = File.new(File.dirname(__FILE__) + '/fixtures/invalid_markup.html', 'r+')
77
+ r = @v.validate_file(fh)
78
+ fh.close
79
+ assert_equal 1, r.errors.length
80
+ end
81
+
82
+
83
+ def test_validator_abort
84
+ @v.set_debug!
85
+ assert_nothing_raised do
86
+ r = @v.validate_uri('http://code.dunae.ca/w3c_validators/test/invalid_encoding.html')
87
+ assert !r.is_valid?
88
+ assert_equal 1, r.errors.length
89
+ assert_equal [], r.warnings
90
+ end
91
+ end
92
+
93
+
94
+ end