html-proofer 2.4.2 → 2.5.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.
@@ -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