diff --git a/lib/html-proofer/cache.rb b/lib/html-proofer/cache.rb index 2e143f79..18304320 100644 --- a/lib/html-proofer/cache.rb +++ b/lib/html-proofer/cache.rb @@ -109,8 +109,13 @@ def detect_url_changes(found) additions end + # TODO: Garbage performance--both the external and internal + # caches need access to this file. Write a proper versioned + # schema in the future def write - File.write(cache_file, @cache_log.to_json) + file = {} + file = JSON.parse(File.read(cache_file)) if File.exist?(cache_file) + File.write(cache_file, file.merge(@cache_log).to_json) end def load? diff --git a/lib/html-proofer/check/links.rb b/lib/html-proofer/check/links.rb index 5754b692..27b7b3a2 100644 --- a/lib/html-proofer/check/links.rb +++ b/lib/html-proofer/check/links.rb @@ -59,11 +59,8 @@ def run add_to_external_urls(@link.href || @link.src) next elsif @link.internal? - if @link.exists? || @link.hash - add_to_internal_urls(@link.href, InternalLink.new(@link, @path, line, content)) - else - add_issue("internally linking to #{@link.href}, which does not exist", line: line, content: content) - end + add_to_internal_urls(@link.href, InternalLink.new(@link, @path, line, content)) + add_issue("internally linking to #{@link.href}, which does not exist", line: line, content: content) if !@link.exists? && !@link.hash end end diff --git a/lib/html-proofer/element.rb b/lib/html-proofer/element.rb index fc3a5f65..ab781c51 100644 --- a/lib/html-proofer/element.rb +++ b/lib/html-proofer/element.rb @@ -24,7 +24,7 @@ def initialize(obj, check, logger) raise e end - @aria_hidden = defined?(@aria_hidden) && @aria_hidden == 'true' ? true : false + @aria_hidden = defined?(@aria_hidden) && @aria_hidden == 'true' @data_proofer_ignore = defined?(@data_proofer_ignore) diff --git a/lib/html-proofer/runner.rb b/lib/html-proofer/runner.rb index 08480cf9..4ce75018 100644 --- a/lib/html-proofer/runner.rb +++ b/lib/html-proofer/runner.rb @@ -63,7 +63,9 @@ def check_list_of_links swap(url, @options[:url_swap]) end end - @external_urls = Hash[*@src.map { |s| [s, nil] }.flatten] + @external_urls = @src.each_with_object({}) do |url, hash| + hash[url] = nil + end validate_external_urls end @@ -123,7 +125,7 @@ def check_parsed(html, path) end external_urls = check.external_urls - external_urls = Hash[check.external_urls.map { |url, file| [swap(url, @options[:url_swap]), file] }] if @options[:url_swap] + external_urls = check.external_urls.map { |url, file| [swap(url, @options[:url_swap]), file] }.to_h if @options[:url_swap] result[:external_urls].merge!(external_urls) result[:failures].concat(check.issues) end diff --git a/lib/html-proofer/url_validator.rb b/lib/html-proofer/url_validator.rb index 4cf85c11..4f1e3b16 100644 --- a/lib/html-proofer/url_validator.rb +++ b/lib/html-proofer/url_validator.rb @@ -85,7 +85,7 @@ def extract_domain_path(uri) # for HEAD. If we've decided to check for hashes, we must do a GET--HEAD is # not available as an option. def external_link_checker(external_urls) - external_urls = Hash[external_urls.sort] + external_urls = external_urls.sort.to_h count = external_urls.length check_text = pluralize(count, 'external link', 'external links') diff --git a/spec/html-proofer/cache_spec.rb b/spec/html-proofer/cache_spec.rb index 7c5b97a0..1ec5489a 100644 --- a/spec/html-proofer/cache_spec.rb +++ b/spec/html-proofer/cache_spec.rb @@ -247,4 +247,58 @@ def read_cache(cache_file) Timecop.return end end + + it 'does recheck failures, regardless of external-only cache' do + cache_file_name = '.external.log' + cache_location = File.join(storage_dir, cache_file_name) + + file = "#{FIXTURES_DIR}/cache/external_example.html" + + new_time = Time.local(2021, 0o1, 27, 12, 0, 0) + Timecop.freeze(new_time) + + File.delete(cache_location) if File.exist?(cache_location) + + run_proofer(file, :file, external_only: true, links_only: true, cache: { timeframe: '1d', cache_file: cache_file_name }.merge(default_cache_options)) + + cache = JSON.parse(File.read(cache_location)) + + expect(cache.keys.count).to eq(1) + expect(cache.keys[0]).to eq('https://github.com/gjtorikian/html-proofer') + + run_proofer(file, :file, links_only: true, cache: { timeframe: '1d', cache_file: cache_file_name }.merge(default_cache_options)) + + cache = JSON.parse(File.read(cache_location)) + expect(cache.keys.count).to eq(1) + expect(cache.keys[0]).to eq('https://github.com/gjtorikian/html-proofer') + + Timecop.return + end + + it 'does recheck failures, regardless of external and internal cache' do + cache_file_name = '.internal_and_external.log' + cache_location = File.join(storage_dir, cache_file_name) + + file = "#{FIXTURES_DIR}/cache/internal_and_external_example.html" + + new_time = Time.local(2021, 0o1, 27, 12, 0, 0) + Timecop.freeze(new_time) + + File.delete(cache_location) if File.exist?(cache_location) + + run_proofer(file, :file, external_only: true, links_only: true, cache: { timeframe: '1d', cache_file: cache_file_name }.merge(default_cache_options)) + + cache = JSON.parse(File.read(cache_location)) + + expect(cache.keys.count).to eq(1) + expect(cache.keys[0]).to eq('https://github.com/gjtorikian/html-proofer') + + run_proofer(file, :file, links_only: true, cache: { timeframe: '1d', cache_file: cache_file_name }.merge(default_cache_options)) + + cache = JSON.parse(File.read(cache_location)) + expect(cache.keys.count).to eq(2) + expect(cache.keys[0]).to eq('https://github.com/gjtorikian/html-proofer') + + Timecop.return + end end diff --git a/spec/html-proofer/fixtures/cache/.external.log b/spec/html-proofer/fixtures/cache/.external.log new file mode 100644 index 00000000..d147adeb --- /dev/null +++ b/spec/html-proofer/fixtures/cache/.external.log @@ -0,0 +1 @@ +{"https://github.com/gjtorikian/html-proofer":{"time":"2021-01-27 12:00:00 +0000","filenames":["spec/html-proofer/fixtures/cache/external_example.html"],"status":200,"message":""}} \ No newline at end of file diff --git a/spec/html-proofer/fixtures/cache/.internal_and_external.log b/spec/html-proofer/fixtures/cache/.internal_and_external.log new file mode 100644 index 00000000..17c57ce2 --- /dev/null +++ b/spec/html-proofer/fixtures/cache/.internal_and_external.log @@ -0,0 +1 @@ +{"https://github.com/gjtorikian/html-proofer":{"time":"2021-01-27 12:00:00 +0000","filenames":["spec/html-proofer/fixtures/cache/internal_and_external_example.html"],"status":200,"message":""},"/somewhere.html":{"time":"2021-01-27 12:00:00 +0000","filenames":["spec/html-proofer/fixtures/cache/internal_and_external_example.html"],"status":200,"message":""}} \ No newline at end of file diff --git a/spec/html-proofer/fixtures/cache/external_example.html b/spec/html-proofer/fixtures/cache/external_example.html new file mode 100644 index 00000000..09c7add9 --- /dev/null +++ b/spec/html-proofer/fixtures/cache/external_example.html @@ -0,0 +1,11 @@ + + + + Example + + + The amazing HTMLProofer + + diff --git a/spec/html-proofer/fixtures/cache/internal_and_external_example.html b/spec/html-proofer/fixtures/cache/internal_and_external_example.html new file mode 100644 index 00000000..1e486d38 --- /dev/null +++ b/spec/html-proofer/fixtures/cache/internal_and_external_example.html @@ -0,0 +1,13 @@ + + + + Example + + + The amazing HTMLProofer + + Somewhere + + diff --git a/spec/html-proofer/fixtures/vcr_cassettes/cache/external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml b/spec/html-proofer/fixtures/vcr_cassettes/cache/external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml new file mode 100644 index 00000000..07a15211 --- /dev/null +++ b/spec/html-proofer/fixtures/vcr_cassettes/cache/external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: head + uri: https://github.com/gjtorikian/html-proofer + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; HTML Proofer/3.18.5; +https://github.com/gjtorikian/html-proofer) + Accept: + - application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + server: + - GitHub.com + date: + - Sun, 21 Feb 2021 16:32:07 GMT + content-type: + - text/html; charset=utf-8 + vary: + - X-PJAX, Accept-Encoding, Accept, X-Requested-With + etag: + - W/"caacf53a5a6580b9579b48c4e7c35847" + cache-control: + - max-age=0, private, must-revalidate + strict-transport-security: + - max-age=31536000; includeSubdomains; preload + x-frame-options: + - deny + x-content-type-options: + - nosniff + x-xss-protection: + - 1; mode=block + referrer-policy: + - no-referrer-when-downgrade + expect-ct: + - max-age=2592000, report-uri="https://api.github.com/_private/browser/errors" + content-security-policy: + - 'default-src ''none''; base-uri ''self''; block-all-mixed-content; connect-src + ''self'' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com + github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com + github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com + cdn.optimizely.com logx.optimizely.com/v1/events wss://alive.github.com online.visualstudio.com/api/v1/locations; + font-src github.githubassets.com; form-action ''self'' github.com gist.github.com; + frame-ancestors ''none''; frame-src render.githubusercontent.com; img-src + ''self'' data: github.githubassets.com identicons.github.com collector.githubapp.com + github-cloud.s3.amazonaws.com user-images.githubusercontent.com/ *.githubusercontent.com; + manifest-src ''self''; media-src ''none''; script-src github.githubassets.com; + style-src ''unsafe-inline'' github.githubassets.com; worker-src github.com/socket-worker-5029ae85.js + gist.github.com/socket-worker-5029ae85.js' + set-cookie: + - _gh_sess=FVxpDGIyyH6A21E%2FHQIg7c18Or8RAQkAqP7S77pa12Gdkt2ht0muVqUEqOFoBCJJI8izF1QaH7NCYDWku3Hk50ic5uaGJnjAPFWI2kX5q6OYQhseU33bO7q0r%2Fl6kdojyfURMLs8Ic7zzZ%2BJxdLlZh8xS0KEeVpX1omvlULMT169y0322Wrsw9YtBBgEtMHinHsyWBDkjSwZxh5y9%2BW7jFx4vPMvBlnao7RTHRAoSPQ3SFW2SdrE76IuSFt89Y7VGOn0EKD%2Fb71JU0Byv8xK1w%3D%3D--8NQJ2ZGl9U%2FqC3M5--1dw7YpQmajE2qqT5%2FQDmug%3D%3D; + Path=/; HttpOnly; Secure; SameSite=Lax + - _octo=GH1.1.168931837.1613925128; Path=/; Domain=github.com; Expires=Mon, + 21 Feb 2022 16:32:08 GMT; Secure; SameSite=Lax + - logged_in=no; Path=/; Domain=github.com; Expires=Mon, 21 Feb 2022 16:32:08 + GMT; HttpOnly; Secure; SameSite=Lax + accept-ranges: + - bytes + x-github-request-id: + - EA4B:61B0:217BDFF:269EF9E:60328B08 + body: + encoding: UTF-8 + string: '' + http_version: '2' + adapter_metadata: + effective_url: https://github.com/gjtorikian/html-proofer + recorded_at: Wed, 27 Jan 2021 12:00:00 GMT +recorded_with: VCR 2.9.3 diff --git a/spec/html-proofer/fixtures/vcr_cassettes/cache/internal_and_external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_internal_and_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml b/spec/html-proofer/fixtures/vcr_cassettes/cache/internal_and_external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_internal_and_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml new file mode 100644 index 00000000..7af71169 --- /dev/null +++ b/spec/html-proofer/fixtures/vcr_cassettes/cache/internal_and_external_example_html_external_only_true_links_only_true_cache_timeframe_1d_cache_file_internal_and_external_log_storage_dir_spec/html-proofer/fixtures/cache_log_level_error_type_file_.yml @@ -0,0 +1,76 @@ +--- +http_interactions: +- request: + method: head + uri: https://github.com/gjtorikian/html-proofer + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - Mozilla/5.0 (compatible; HTML Proofer/3.18.5; +https://github.com/gjtorikian/html-proofer) + Accept: + - application/xml,application/xhtml+xml,text/html;q=0.9, text/plain;q=0.8,image/png,*/*;q=0.5 + Expect: + - '' + response: + status: + code: 200 + message: '' + headers: + server: + - GitHub.com + date: + - Sun, 21 Feb 2021 16:32:07 GMT + content-type: + - text/html; charset=utf-8 + vary: + - X-PJAX, Accept-Encoding, Accept, X-Requested-With + etag: + - W/"caacf53a5a6580b9579b48c4e7c35847" + cache-control: + - max-age=0, private, must-revalidate + strict-transport-security: + - max-age=31536000; includeSubdomains; preload + x-frame-options: + - deny + x-content-type-options: + - nosniff + x-xss-protection: + - 1; mode=block + referrer-policy: + - no-referrer-when-downgrade + expect-ct: + - max-age=2592000, report-uri="https://api.github.com/_private/browser/errors" + content-security-policy: + - 'default-src ''none''; base-uri ''self''; block-all-mixed-content; connect-src + ''self'' uploads.github.com www.githubstatus.com collector.githubapp.com api.github.com + github-cloud.s3.amazonaws.com github-production-repository-file-5c1aeb.s3.amazonaws.com + github-production-upload-manifest-file-7fdce7.s3.amazonaws.com github-production-user-asset-6210df.s3.amazonaws.com + cdn.optimizely.com logx.optimizely.com/v1/events wss://alive.github.com online.visualstudio.com/api/v1/locations; + font-src github.githubassets.com; form-action ''self'' github.com gist.github.com; + frame-ancestors ''none''; frame-src render.githubusercontent.com; img-src + ''self'' data: github.githubassets.com identicons.github.com collector.githubapp.com + github-cloud.s3.amazonaws.com user-images.githubusercontent.com/ *.githubusercontent.com; + manifest-src ''self''; media-src ''none''; script-src github.githubassets.com; + style-src ''unsafe-inline'' github.githubassets.com; worker-src github.com/socket-worker-5029ae85.js + gist.github.com/socket-worker-5029ae85.js' + set-cookie: + - _gh_sess=46RoTpx1xZeM6V2gyQ6RUJMiXmvy%2BbxrEcxPIktSfACs4M3XPgtmOKjITU9cFKOkqb21u%2FA25dQxwAv7y7KInm6XJK3P6xEc9uPTU65y8vpt%2FRN9QK9cfcEyhRQ2k7T1TRRltkalm0G3nOhNZnb1EoUcibLEuDYkJwpTFVBagH55cOSsINz%2BjwLd4dd4BO6k3tV2HjKE6YVHZdGeaq1sIxth7n8LDrp8916Ygm0TAVy7UG71UZC2cQPwz0pRu9VwYUpuDc0PzXWNwsz2Ey74bg%3D%3D--jSHIFDa3UTkzqp%2BP--adTIajHGBxFj%2FQ332TDFrw%3D%3D; + Path=/; HttpOnly; Secure; SameSite=Lax + - _octo=GH1.1.1571982095.1613925127; Path=/; Domain=github.com; Expires=Mon, + 21 Feb 2022 16:32:07 GMT; Secure; SameSite=Lax + - logged_in=no; Path=/; Domain=github.com; Expires=Mon, 21 Feb 2022 16:32:07 + GMT; HttpOnly; Secure; SameSite=Lax + accept-ranges: + - bytes + x-github-request-id: + - EA49:128D6:119821F:145FC7E:60328B07 + body: + encoding: UTF-8 + string: '' + http_version: '2' + adapter_metadata: + effective_url: https://github.com/gjtorikian/html-proofer + recorded_at: Wed, 27 Jan 2021 12:00:00 GMT +recorded_with: VCR 2.9.3