html-proofer 2.4.2 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,21 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class FaviconCheckable < ::HTML::Proofer::Checkable
4
- def rel
5
- @rel
6
- end
4
+ attr_reader :rel
7
5
  end
8
6
 
9
7
  class FaviconCheck < ::HTML::Proofer::CheckRunner
10
-
11
8
  def run
12
- @html.xpath('//link[not(ancestor::pre or ancestor::code)]').each do |favicon|
13
- favicon = FaviconCheckable.new favicon, self
9
+ found = false
10
+ @html.xpath('//link[not(ancestor::pre or ancestor::code)]').each do |node|
11
+ favicon = FaviconCheckable.new(node, self)
14
12
  next if favicon.ignore?
15
- return if favicon.rel.split(' ').last.eql? 'icon'
13
+ found = true if favicon.rel.split(' ').last.eql? 'icon'
14
+ break if found
16
15
  end
17
16
 
17
+ return if found
18
+
18
19
  add_issue 'no favicon specified'
19
20
  end
20
-
21
21
  end
@@ -27,16 +27,18 @@ class HtmlCheck < ::HTML::Proofer::CheckRunner
27
27
  rect set stop switch symbol text textPath tref tspan use
28
28
  view vkern)
29
29
 
30
+ SCRIPT_EMBEDS_MSG = /Element script embeds close tag/
31
+
30
32
  def run
31
- @html.errors.each do |e|
32
- message = e.message
33
- line = e.line
33
+ @html.errors.each do |error|
34
+ message = error.message
35
+ line = error.line
34
36
  # Nokogiri (or rather libxml2 underhood) only recognizes html4 tags,
35
37
  # so we need to skip errors caused by the new tags in html5
36
38
  next if HTML5_TAGS.include? message[/Tag ([\w-]+) invalid/o, 1]
37
39
 
38
40
  # tags embedded in scripts are used in templating languages: http://git.io/vOovv
39
- next if @validation_opts[:ignore_script_embeds] && message =~ /Element script embeds close tag/
41
+ next if @validation_opts[:ignore_script_embeds] && message =~ SCRIPT_EMBEDS_MSG
40
42
 
41
43
  add_issue(message, line)
42
44
  end
@@ -1,12 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  class ImageCheckable < ::HTML::Proofer::Checkable
4
-
5
4
  SCREEN_SHOT_REGEX = /Screen(?: |%20)Shot(?: |%20)\d+-\d+-\d+(?: |%20)at(?: |%20)\d+.\d+.\d+/
6
5
 
7
- def alt
8
- @alt
9
- end
6
+ attr_reader :alt
10
7
 
11
8
  def empty_alt_tag?
12
9
  alt.strip.empty?
@@ -23,32 +20,32 @@ class ImageCheckable < ::HTML::Proofer::Checkable
23
20
  def missing_src?
24
21
  !src
25
22
  end
26
-
27
23
  end
28
24
 
29
25
  class ImageCheck < ::HTML::Proofer::CheckRunner
30
26
  def run
31
- @html.css('img').each do |i|
32
- img = ImageCheckable.new i, self
27
+ @html.css('img').each do |node|
28
+ img = ImageCheckable.new(node, self)
29
+ line = node.line
33
30
 
34
31
  next if img.ignore?
35
32
 
36
33
  # screenshot filenames should return because of terrible names
37
- next add_issue("image has a terrible filename (#{img.src})", i.line) if img.terrible_filename?
34
+ next add_issue("image has a terrible filename (#{img.src})", line) if img.terrible_filename?
38
35
 
39
36
  # does the image exist?
40
37
  if img.missing_src?
41
- add_issue('image has no src or srcset attribute', i.line)
38
+ add_issue('image has no src or srcset attribute', line)
42
39
  else
43
40
  if img.remote?
44
- add_to_external_urls img.src
41
+ add_to_external_urls(img.src, line)
45
42
  else
46
- add_issue("internal image #{img.src} does not exist", i.line) unless img.exists?
43
+ add_issue("internal image #{img.src} does not exist", line) unless img.exists?
47
44
  end
48
45
  end
49
46
 
50
47
  if img.alt.nil? || (img.empty_alt_tag? && !img.ignore_empty_alt?)
51
- add_issue("image #{img.src} does not have an alt attribute", i.line)
48
+ add_issue("image #{img.src} does not have an alt attribute", line)
52
49
  end
53
50
  end
54
51
 
@@ -19,15 +19,15 @@ class LinkCheckable < ::HTML::Proofer::Checkable
19
19
  def placeholder?
20
20
  (id || name) && href.nil?
21
21
  end
22
-
23
22
  end
24
23
 
25
24
  class LinkCheck < ::HTML::Proofer::CheckRunner
26
- include HTML::Utils
25
+ include HTML::Proofer::Utils
27
26
 
28
27
  def run
29
- @html.css('a, link').each do |l|
30
- link = LinkCheckable.new l, self
28
+ @html.css('a, link').each do |node|
29
+ link = LinkCheckable.new(node, self)
30
+ line = node.line
31
31
 
32
32
  next if link.ignore?
33
33
  next if link.href =~ /^javascript:/ # can't put this in ignore? because the URI does not parse
@@ -35,24 +35,15 @@ class LinkCheck < ::HTML::Proofer::CheckRunner
35
35
 
36
36
  # is it even a valid URL?
37
37
  unless link.valid?
38
- add_issue("#{link.href} is an invalid URL", l.line)
38
+ add_issue("#{link.href} is an invalid URL", line)
39
39
  next
40
40
  end
41
41
 
42
- case link.scheme
43
- when 'mailto'
44
- if link.path.empty?
45
- add_issue("#{link.href} contains no email address", l.line)
46
- elsif !link.path.include?('@')
47
- add_issue("#{link.href} contains an invalid email address", l.line)
48
- end
49
- when 'tel'
50
- add_issue("#{link.href} contains no phone number", l.line) if link.path.empty?
51
- end
42
+ check_schemes(link, line)
52
43
 
53
44
  # is there even a href?
54
45
  if link.missing_href?
55
- add_issue('anchor has no href attribute', l.line)
46
+ add_issue('anchor has no href attribute', line)
56
47
  next
57
48
  end
58
49
 
@@ -61,47 +52,71 @@ class LinkCheck < ::HTML::Proofer::CheckRunner
61
52
 
62
53
  # does the file even exist?
63
54
  if link.remote?
64
- add_to_external_urls link.href
55
+ add_to_external_urls(link.href, line)
65
56
  next
66
57
  elsif !link.internal?
67
- add_issue("internally linking to #{link.href}, which does not exist", l.line) unless link.exists?
58
+ add_issue("internally linking to #{link.href}, which does not exist", line) unless link.exists?
68
59
  end
69
60
 
70
61
  # does the local directory have a trailing slash?
71
62
  if link.unslashed_directory? link.absolute_path
72
- add_issue("internally linking to a directory #{link.absolute_path} without trailing slash", l.line)
63
+ add_issue("internally linking to a directory #{link.absolute_path} without trailing slash", line)
73
64
  next
74
65
  end
75
66
 
76
67
  # verify the target hash
77
- if link.hash
78
- if link.internal?
79
- unless hash_check @html, link.hash
80
- add_issue("linking to internal hash ##{link.hash} that does not exist", l.line)
81
- end
82
- elsif link.external?
83
- external_link_check(link)
84
- end
85
- end
68
+ handle_hash(link, line) if link.hash
86
69
  end
87
70
 
88
71
  external_urls
89
72
  end
90
73
 
91
- def external_link_check(link)
74
+ def check_schemes(link, line)
75
+ case link.scheme
76
+ when 'mailto'
77
+ handle_mailto(link, line)
78
+ when 'tel'
79
+ handle_tel(link, line)
80
+ end
81
+ end
82
+
83
+ def handle_mailto(link, line)
84
+ if link.path.empty?
85
+ add_issue("#{link.href} contains no email address", line)
86
+ elsif !link.path.include?('@')
87
+ add_issue("#{link.href} contains an invalid email address", line)
88
+ end
89
+ end
90
+
91
+ def handle_tel(link, line)
92
+ add_issue("#{link.href} contains no phone number", line) if link.path.empty?
93
+ end
94
+
95
+ def handle_hash(link, line)
96
+ if link.internal?
97
+ unless hash_check @html, link.hash
98
+ add_issue("linking to internal hash ##{link.hash} that does not exist", line)
99
+ end
100
+ elsif link.external?
101
+ external_link_check(link, line)
102
+ end
103
+ end
104
+
105
+ def external_link_check(link, line)
92
106
  if !link.exists?
93
- add_issue("trying to find hash of #{link.href}, but #{link.absolute_path} does not exist", link.line)
107
+ add_issue("trying to find hash of #{link.href}, but #{link.absolute_path} does not exist", line)
94
108
  else
95
109
  target_html = create_nokogiri link.absolute_path
96
110
  unless hash_check target_html, link.hash
97
- add_issue("linking to #{link.href}, but #{link.hash} does not exist", link.line)
111
+ add_issue("linking to #{link.href}, but #{link.hash} does not exist", line)
98
112
  end
99
113
  end
100
114
  end
101
115
 
102
116
  def hash_check(html, href_hash)
103
117
  html.xpath("//*[case_insensitive_equals(@id, '#{href_hash}')]", \
104
- "//*[case_insensitive_equals(@name, '#{href_hash}')]", HTML::Proofer::XpathFunctions.new).length > 0
118
+ "//*[case_insensitive_equals(@name, '#{href_hash}')]", \
119
+ HTML::Proofer::XpathFunctions.new).length > 0
105
120
  end
106
121
 
107
122
  end
@@ -18,19 +18,20 @@ end
18
18
 
19
19
  class ScriptCheck < ::HTML::Proofer::CheckRunner
20
20
  def run
21
- @html.css('script').each do |s|
22
- script = ScriptCheckable.new s, self
21
+ @html.css('script').each do |node|
22
+ script = ScriptCheckable.new(node, self)
23
+ line = node.line
23
24
 
24
25
  next if script.ignore?
25
26
  next unless script.blank?
26
27
 
27
28
  # does the script exist?
28
29
  if script.missing_src?
29
- add_issue('script is empty and has no src attribute', s.line)
30
+ add_issue('script is empty and has no src attribute', line)
30
31
  elsif script.remote?
31
- add_to_external_urls script.src
32
+ add_to_external_urls(script.src, line)
32
33
  else
33
- add_issue("internal script #{script.src} does not exist", s.line) unless script.exists?
34
+ add_issue("internal script #{script.src} does not exist", line) unless script.exists?
34
35
  end
35
36
  end
36
37
 
@@ -6,8 +6,12 @@ module HTML
6
6
  class Log
7
7
  include Yell::Loggable
8
8
 
9
- def initialize(verbose)
10
- log_level = verbose ? :debug : :info
9
+ def initialize(verbose, verbosity = nil)
10
+ log_level = if verbosity.nil?
11
+ verbose ? :debug : :info
12
+ else
13
+ verbosity
14
+ end
11
15
 
12
16
  @logger = Yell.new(:format => false, \
13
17
  :name => 'HTML::Proofer', \
@@ -5,7 +5,7 @@ require_relative './utils'
5
5
  module HTML
6
6
  class Proofer
7
7
  class UrlValidator
8
- include Utils
8
+ include HTML::Proofer::Utils
9
9
 
10
10
  attr_accessor :logger, :external_urls, :hydra
11
11
 
@@ -74,6 +74,7 @@ module HTML
74
74
  href = response.request.base_url.to_s
75
75
  method = response.request.options[:method]
76
76
  response_code = response.code
77
+
77
78
  debug_msg = "Received a #{response_code} for #{href}"
78
79
  debug_msg << " in #{filenames.join(' ')}" unless filenames.nil?
79
80
  logger.log :debug, :yellow, debug_msg
@@ -87,7 +88,7 @@ module HTML
87
88
  else
88
89
  return if @options[:only_4xx] && !response_code.between?(400, 499)
89
90
  # Received a non-successful http response.
90
- add_failed_tests filenames, "External link #{href} failed: #{response_code} #{response.return_message}", response_code
91
+ add_external_issue(filenames, "External link #{href} failed: #{response_code} #{response.return_message}", response_code)
91
92
  end
92
93
  end
93
94
 
@@ -108,15 +109,15 @@ module HTML
108
109
 
109
110
  return unless body_doc.xpath(xpath).empty?
110
111
 
111
- add_failed_tests filenames, "External link #{href} failed: #{effective_url} exists, but the hash '#{hash}' does not", response.code
112
+ add_external_issue filenames, "External link #{href} failed: #{effective_url} exists, but the hash '#{hash}' does not", response.code
112
113
  end
113
114
 
114
115
  def handle_timeout(href, filenames, response_code)
115
116
  return if @options[:only_4xx]
116
- add_failed_tests filenames, "External link #{href} failed: got a time out", response_code
117
+ add_external_issue filenames, "External link #{href} failed: got a time out", response_code
117
118
  end
118
119
 
119
- def add_failed_tests(filenames, desc, status = nil)
120
+ def add_external_issue(filenames, desc, status = nil)
120
121
  if filenames.nil?
121
122
  @failed_tests << CheckRunner::Issue.new('', desc, nil, status)
122
123
  else
@@ -129,7 +130,6 @@ module HTML
129
130
  rescue URI::InvalidURIError
130
131
  nil
131
132
  end
132
-
133
133
  end
134
134
  end
135
135
  end
@@ -1,24 +1,26 @@
1
1
  require 'nokogiri'
2
2
 
3
3
  module HTML
4
- module Utils
5
- def create_nokogiri(path)
6
- if File.exist? path
7
- content = File.open(path).read
8
- else
9
- content = path
10
- end
4
+ class Proofer
5
+ module Utils
6
+ def create_nokogiri(path)
7
+ if File.exist? path
8
+ content = File.open(path).read
9
+ else
10
+ content = path
11
+ end
11
12
 
12
- Nokogiri::HTML(content)
13
- end
14
- module_function :create_nokogiri
13
+ Nokogiri::HTML(content)
14
+ end
15
+ module_function :create_nokogiri
15
16
 
16
- def swap(href, replacement)
17
- replacement.each do |link, replace|
18
- href = href.gsub(link, replace)
17
+ def swap(href, replacement)
18
+ replacement.each do |link, replace|
19
+ href = href.gsub(link, replace)
20
+ end
21
+ href
19
22
  end
20
- href
23
+ module_function :swap
21
24
  end
22
- module_function :swap
23
25
  end
24
26
  end
@@ -1,5 +1,5 @@
1
1
  module HTML
2
2
  class Proofer
3
- VERSION = '2.4.2'
3
+ VERSION = '2.5.0'
4
4
  end
5
5
  end
@@ -0,0 +1,8 @@
1
+ <a href="https://github.com/contact?form%5Bsubject%5D=New+Assigned+Events">First</a>
2
+
3
+ <a href="https://github.com/contact?form%5Bsubject%5D=Organization+and+Team+Membership+APIs">Ignored</a>
4
+
5
+ <a href="https://github.com/contact#false?form%5Bsubject%5D=Organization+and+Team+Membership+APIs">Second, because of hash</a>
6
+
7
+
8
+ <a href="https://github.com/contact?form%5Bsubject%5D=Blah+and+blah+blah+APIs">Ignored</a>
@@ -0,0 +1,87 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: head
5
+ uri: https://github.com/contact#false?form%5Bsubject%5D=Organization+and+Team+Membership+APIs
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Mozilla/5.0 (compatible; HTML Proofer/2.4.2; +https://github.com/gjtorikian/html-proofer)
12
+ response:
13
+ status:
14
+ code: 0
15
+ message:
16
+ headers: {}
17
+ body:
18
+ encoding: UTF-8
19
+ string: ''
20
+ http_version:
21
+ adapter_metadata:
22
+ effective_url: https://github.com/contact
23
+ recorded_at: Fri, 04 Sep 2015 21:28:18 GMT
24
+ - request:
25
+ method: head
26
+ uri: https://github.com/contact?form%5Bsubject%5D=New+Assigned+Events
27
+ body:
28
+ encoding: US-ASCII
29
+ string: ''
30
+ headers:
31
+ User-Agent:
32
+ - Mozilla/5.0 (compatible; HTML Proofer/2.4.2; +https://github.com/gjtorikian/html-proofer)
33
+ response:
34
+ status:
35
+ code: 0
36
+ message:
37
+ headers: {}
38
+ body:
39
+ encoding: UTF-8
40
+ string: ''
41
+ http_version:
42
+ adapter_metadata:
43
+ effective_url: https://github.com/contact?form%5Bsubject%5D=New+Assigned+Events
44
+ recorded_at: Fri, 04 Sep 2015 21:28:18 GMT
45
+ - request:
46
+ method: get
47
+ uri: https://github.com/contact#false?form%5Bsubject%5D=Organization+and+Team+Membership+APIs
48
+ body:
49
+ encoding: US-ASCII
50
+ string: ''
51
+ headers:
52
+ User-Agent:
53
+ - Mozilla/5.0 (compatible; HTML Proofer/2.4.2; +https://github.com/gjtorikian/html-proofer)
54
+ response:
55
+ status:
56
+ code: 0
57
+ message:
58
+ headers: {}
59
+ body:
60
+ encoding: UTF-8
61
+ string: ''
62
+ http_version:
63
+ adapter_metadata:
64
+ effective_url: https://github.com/contact
65
+ recorded_at: Fri, 04 Sep 2015 21:28:18 GMT
66
+ - request:
67
+ method: get
68
+ uri: https://github.com/contact?form%5Bsubject%5D=New+Assigned+Events
69
+ body:
70
+ encoding: US-ASCII
71
+ string: ''
72
+ headers:
73
+ User-Agent:
74
+ - Mozilla/5.0 (compatible; HTML Proofer/2.4.2; +https://github.com/gjtorikian/html-proofer)
75
+ response:
76
+ status:
77
+ code: 0
78
+ message:
79
+ headers: {}
80
+ body:
81
+ encoding: UTF-8
82
+ string: ''
83
+ http_version:
84
+ adapter_metadata:
85
+ effective_url: https://github.com/contact?form%5Bsubject%5D=New+Assigned+Events
86
+ recorded_at: Fri, 04 Sep 2015 21:28:18 GMT
87
+ recorded_with: VCR 2.9.3