forked from gjtorikian/html-proofer
/
runner.rb
173 lines (146 loc) · 5.62 KB
/
runner.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
module HTMLProofer
class Runner
include HTMLProofer::Utils
attr_reader :options, :external_urls
def initialize(src, opts = {})
@src = src
@options = HTMLProofer::Configuration::PROOFER_DEFAULTS.merge(opts)
@options[:typhoeus] = HTMLProofer::Configuration::TYPHOEUS_DEFAULTS.merge(opts[:typhoeus] || {})
@options[:hydra] = HTMLProofer::Configuration::HYDRA_DEFAULTS.merge(opts[:hydra] || {})
@options[:parallel] = HTMLProofer::Configuration::PARALLEL_DEFAULTS.merge(opts[:parallel] || {})
@options[:validation] = HTMLProofer::Configuration::VALIDATION_DEFAULTS.merge(opts[:validation] || {})
@options[:cache] = HTMLProofer::Configuration::CACHE_DEFAULTS.merge(opts[:cache] || {})
@type = @options.delete(:type)
@logger = HTMLProofer::Log.new(@options[:log_level])
# Add swap patterns for internal domains
unless @options[:internal_domains].empty?
@options[:internal_domains].each do |dom|
@options[:url_swap][Regexp.new("^http://#{dom}")] = ''
@options[:url_swap][Regexp.new("^https://#{dom}")] = ''
@options[:url_swap][Regexp.new("^//#{dom}")] = ''
end
end
@failures = []
end
def run
if @type == :links
@logger.log :info, "Running #{checks} on #{@src}... \n\n"
check_list_of_links unless @options[:disable_external]
else
@logger.log :info, "Running #{checks} on #{@src} on *#{@options[:extension]}... \n\n"
check_files
file_text = pluralize(files.length, 'file', 'files')
@logger.log :info, "Ran on #{file_text}!\n\n"
end
if @failures.empty?
@logger.log_with_color :info, :green, 'HTML-Proofer finished successfully.'
else
print_failed_tests
end
end
def check_list_of_links
if @options[:url_swap]
@src = @src.map do |url|
swap(url, @options[:url_swap])
end
end
@external_urls = Hash[*@src.map { |s| [s, nil] }.flatten]
validate_urls
end
# Collects any external URLs found in a directory of files. Also collectes
# every failed test from process_files.
# Sends the external URLs to Typhoeus for batch processing.
def check_files
@external_urls = {}
process_files.each do |item|
@external_urls.merge!(item[:external_urls])
@failures.concat(item[:failures])
end
# TODO: lazy. if we're checking only external links,
# we'll just trash all the failed tests. really, we should
# just not run those other checks at all.
if @options[:external_only]
@failures = []
validate_urls
elsif !@options[:disable_external]
validate_urls
end
end
# Walks over each implemented check and runs them on the files, in parallel.
def process_files
if @options[:parallel].empty?
files.map { |path| check_path(path) }
else
Parallel.map(files, @options[:parallel]) { |path| check_path(path) }
end
end
def check_parsed(html, path)
result = { external_urls: {}, failures: [] }
@src = [@src] if @type == :file
@src.each do |src|
checks.each do |klass|
@logger.log :debug, "Checking #{klass.to_s.downcase} on #{path} ..."
check = Object.const_get(klass).new(src, path, html, @options)
check.run
external_urls = check.external_urls
if @options[:url_swap]
external_urls = Hash[check.external_urls.map { |url, file| [swap(url, @options[:url_swap]), file] }]
end
result[:external_urls].merge!(external_urls)
result[:failures].concat(check.issues)
end
end
result
end
def check_path(path)
check_parsed create_nokogiri(path), path
end
def validate_urls
url_validator = HTMLProofer::UrlValidator.new(@logger, @external_urls, @options)
@failures.concat(url_validator.run)
@external_urls = url_validator.external_urls
end
def files
@files ||= if @type == :directory
@src.map do |src|
pattern = File.join(src, '**', "*#{@options[:extension]}")
files = Dir.glob(pattern).select { |fn| File.file? fn }
files.reject { |f| ignore_file?(f) }
end.flatten
elsif @type == :file && File.extname(@src) == @options[:extension]
[@src].reject { |f| ignore_file?(f) }
else
[]
end
end
def ignore_file?(file)
@options[:file_ignore].each do |pattern|
return true if pattern.is_a?(String) && pattern == file
return true if pattern.is_a?(Regexp) && pattern =~ file
end
false
end
def checks
return @checks if defined?(@checks) && !@checks.nil?
@checks = HTMLProofer::Check.subchecks.map(&:name)
@checks.delete('FaviconCheck') unless @options[:check_favicon]
@checks.delete('HtmlCheck') unless @options[:check_html]
@checks.delete('OpenGraphCheck') unless @options[:check_opengraph]
@options[:checks_to_ignore].each { |ignored| @checks.delete(ignored) }
@checks
end
def failed_tests
result = []
return result if @failures.empty?
@failures.each { |f| result << f.to_s }
result
end
def print_failed_tests
sorted_failures = SortedIssues.new(@failures, @options[:error_sort], @logger)
sorted_failures.sort_and_report
count = @failures.length
failure_text = pluralize(count, 'failure', 'failures')
raise @logger.colorize :red, "HTML-Proofer found #{failure_text}!"
end
end
end