assert_valid_html 0.1.0
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.
@@ -0,0 +1,10 @@
|
|
1
|
+
class ActionController::TestCase
|
2
|
+
def assert_valid_html
|
3
|
+
return unless @response.headers['Content-Type'].match(/html/)
|
4
|
+
|
5
|
+
validator = AssertValidHtml::Validator.new(@response.body)
|
6
|
+
assert_block(validator.message){ validator.valid? }
|
7
|
+
end
|
8
|
+
|
9
|
+
alias_method :assert_valid_markup, :assert_valid_html
|
10
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "open3"
|
2
|
+
|
3
|
+
module AssertValidHtml
|
4
|
+
class Validator
|
5
|
+
ValidationError = Struct.new(:message, :line, :context)
|
6
|
+
|
7
|
+
ALLOWED_ATTRIBUTES = %w[
|
8
|
+
media hreflang rel target value charset autofocus placeholder form
|
9
|
+
required disabled autocomplete min max multiple pattern step list
|
10
|
+
novalidate formaction formenctype formmethod formnovalidate formtarget
|
11
|
+
type label contextmenu scoped async manifest sizes reversed sandbox
|
12
|
+
seamless srcdoc contenteditable draggable hidden role data-\\S* aria-\\S*
|
13
|
+
spellcheck
|
14
|
+
]
|
15
|
+
|
16
|
+
ALLOWED_ELEMENTS = %w[
|
17
|
+
section article aside hgroup header footer nav figure figcaption video
|
18
|
+
audio source embed progress meter time ruby rt rp wbr canvas command
|
19
|
+
details datalist keygen output
|
20
|
+
]
|
21
|
+
|
22
|
+
IGNORED = [
|
23
|
+
/Warning: trimming empty/,
|
24
|
+
/lacks "summary" attribute/,
|
25
|
+
/<meta> lacks "content" attribute/, # HTML5
|
26
|
+
/proprietary attribute "xmlns:fb"/, # Facebook
|
27
|
+
/<\/?fb:/ # //
|
28
|
+
]
|
29
|
+
|
30
|
+
def self.ignore(regexp)
|
31
|
+
ignored << regexp
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.ignored
|
35
|
+
@ignored ||= IGNORED.dup
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ignored_regexp
|
39
|
+
Regexp.new((ignored + [
|
40
|
+
%r{proprietary attribute "(?:#{ALLOWED_ATTRIBUTES.join("|")})"},
|
41
|
+
%r{discarding unexpected </?(?:#{ALLOWED_ELEMENTS.join("|")})>}
|
42
|
+
]).join("|"))
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(html)
|
46
|
+
@html = html
|
47
|
+
@lines = html.split(/\n/)
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid?
|
51
|
+
errors.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def errors
|
55
|
+
@errors ||= (
|
56
|
+
ignored = self.class.ignored_regexp
|
57
|
+
tidy(@html).split(/\n/).select {|w| w =~ /Warning/ && w !~ ignored }
|
58
|
+
).inject([]){ |array, error|
|
59
|
+
line, message = parse_tidy_message(error)
|
60
|
+
array << ValidationError.new(message, line, context(line))
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
def context(line)
|
65
|
+
top = [0, line - 6].max
|
66
|
+
bottom = [[@lines.length - 1, 0].max, line + 4].min
|
67
|
+
(top..bottom).to_a.zip(@lines[top..bottom]).map{ |number, text|
|
68
|
+
"%s %3d | %s" % [number + 1 == line ? "*" : " ", number + 1, text]
|
69
|
+
}.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def message
|
73
|
+
if valid?
|
74
|
+
"HTML is valid"
|
75
|
+
else
|
76
|
+
(["HTML is invalid"] + errors.map{ |e| e.message + "\n" + e.context }).join("\n\n")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def parse_tidy_message(error)
|
82
|
+
m = error.match(/^line (\d+) column (\d+) - (.*)/)
|
83
|
+
return m[1].to_i, "#{m[3]} at line #{m[1]} column #{m[2]}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def tidy(html)
|
87
|
+
stdin, stdout, stderr = Open3.popen3("tidy -q -e -utf8")
|
88
|
+
stdin << html
|
89
|
+
stdin.close
|
90
|
+
stderr.read
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: assert_valid_html
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Paul Battley
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-17 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: pbattley@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/assert_valid_html.rb
|
32
|
+
- lib/assert_valid_html/validator.rb
|
33
|
+
- lib/assert_valid_html/test.rb
|
34
|
+
- lib/assert_valid_html/version.rb
|
35
|
+
- lib/assert_valid_html/railtie.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage:
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
hash: 3
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.7
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Check HTML validity without using an external web service
|
70
|
+
test_files: []
|
71
|
+
|