ITADN
gjtorikian/html-proofer
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

HTMLProofer

如果你生成 HTML 文件,那么这个工具可能适合你

项目范围

HTMLProofer 是一组用于验证 HTML 输出的测试。这些测试检查你的图像引用是否合法、是否包含 alt 标签、内部链接是否正常工作,等等。它旨在成为针对你输出的一体化检查器。

本项目范围内包括任何用于 HTML 文档质量的知名且广泛使用的测试。本项目的一个主要用途是持续集成——因此我们必须拥有可靠的结果。我们通常将正确性置于性能之上。并且,如有必要,我们应该能够将此程序对 HTML 错误的检测追溯到已记录的最佳实践或标准,例如 W3 规范。

第三方模块。 我们希望此产品对持续集成有用,因此我们倾向于避免容易产生误报结果的主观测试,例如拼写检查器、缩进检查器等。如果你想处理这些项目,请参阅自定义测试部分],并考虑以第三方模块的形式添加一个实现。

高级配置。 大多数前端开发人员可以使用我们的命令行程序]来测试他们的 HTML。高级配置将需要使用 Ruby。

安装

将此行添加到你的应用程序的 Gemfile 中:

gem 'html-proofer'

然后执行:

$ bundle install

或者自行安装:

$ gem install html-proofer

注意: 当安装速度至关重要时,请在您的环境中将 NOKOGIRI_USE_SYSTEM_LIBRARIES 设置为 true。这对于提高持续集成构建的速度非常有用。

测试内容?

以下是 HTMLProofer 可以执行的大致全面的检查列表。

图像

img 元素:

  • 您的所有图像是否都有 alt 标签
  • 您的内部图像引用是否未损坏
  • 外部图像是否正在显示
  • 您的图像是否为 HTTP

链接

alink 元素:

  • 您的内部链接是否正常工作
  • 您的内部哈希引用(#linkToMe)是否正常工作
  • 外部链接是否正常工作
  • 您的链接是否为 HTTPS
  • 是否启用了 CORS/SRI

脚本

script 元素:

  • 您的内部脚本引用是否正常工作
  • 外部脚本是否正在加载
  • 是否启用了 CORS/SRI

图标

  • 您的 favicon 是否有效。

OpenGraph

  • OpenGraph 元数据中的图像和 URL 是否有效。

用法

您可以配置 HTMLProofer 在以下位置运行:

  • 一个文件
  • 一个目录
  • 一个目录数组
  • 一个链接数组

它也可以通过命令行运行。

检查单个文件

如果您只想检查单个文件,请使用 check_file 方法:

HTMLProofer.check_file("/path/to/a/file.html").run

检查目录

如果您想检查一个目录,请使用 check_directory

HTMLProofer.check_directory("./out").run

如果要检查多个目录,请使用 check_directories

HTMLProofer.check_directories(["./one", "./two"]).run

检查链接数组

使用 check_links,你还可以传入一个链接数组:

HTMLProofer.check_links(["https://github.com", "https://jekyllrb.com"]).run

交换信息

有时,您 HTML 中的信息与服务器提供的内容并不一致。在这种情况下,您可以使用 swap_urls 将文件中的 URL 映射为您希望其变成的 URL。例如:

run_proofer(file, :file, swap_urls: { %r{^https//placeholder.com} => "https://website.com" })

在这种情况下,任何匹配 ^https://placeholder.com 的链接都会被转换为 https://website.com

类似的交换过程也可以用于属性:

run_proofer(file, :file, swap_attributes: { "img"  => [["data-src", "src"]] })

在这种情况下,我们是在告诉 HTMLProofer,对于检测到的任何 img 标签,对于任何 src 属性,都将其视为实际上是 src 属性。由于值是一个数组的数组,你可以为每个元素传入任意数量的属性替换。

在命令行中使用

使用此 gem 时,你还会获得一个名为 htmlproofer 的新程序。太棒了!

通过命令行以标志的形式传入选项,如下所示:

htmlproofer --extensions .html.erb ./out

使用 htmlproofer --help 查看所有命令行选项。

命令行的特殊情况

对于需要输入数组的选项,请用引号将值括起来,并且不要使用 任何空格。例如,要排除一个 HTTP 状态码数组,你可以这样做:

htmlproofer --ignore-status-codes "999,401,404" ./out

对于类似 url-ignore 以及其他需要正则表达式数组的选项, 你可以传入如下语法:

htmlproofer --ignore-urls "/www.github.com/,/foo.com/" ./out

由于 swap_urls 有点特殊,你将传入一对 RegEx:String 值。转义序列 \: 应用于生成字面量 :s htmlproofer 会弄清楚你的意思。

htmlproofer --swap-urls "wow:cow,mow:doh" --extensions .html.erb --ignore-urls www.github.com ./out

某些配置选项,例如 --typheous--cache--swap-attributes,需要格式正确的 JSON。

针对 baseurl 进行调整

如果您的 Jekyll 站点配置了 baseurl,则需要调整生成的 url 验证以适配该配置。最简单的方法是使用 swap_urls 选项。

对于值为 /BASEURLsite.baseurl,其在命令行上的形式如下:

htmlproofer --assume-extension ./_site --swap-urls '^/BASEURL/:/'

或在你的 Rakefile

require "html-proofer"

task :test do
  sh "bundle exec jekyll build"
  options = { swap_urls: "^/BASEURL/:/" }
  HTMLProofer.check_directory("./_site", options).run
end

通过 Docker 使用

如果您在安装 Ruby/Nokogumbo 时遇到问题(或不想安装),可以通过 Docker 运行该命令行工具。更多信息请参阅 klakegg/html-proofer

忽略内容

向任意标签添加 data-proofer-ignore 属性,即可在所有检查中忽略该标签。

<a href="https://notareallink" data-proofer-ignore>Not checked.</a>

这同样适用于父元素,一直向上到 <html> 标签:

<div data-proofer-ignore>
  <a href="https://notareallink">Not checked because of parent.</a>
</div>

忽略新文件

假设你的拉取请求中有一些新文件,而你的测试失败是因为指向这些文件的链接尚未生效。你可以做的其中一件事是,针对你的基础分支运行 diff,并显式忽略这些新文件,如下所示:

directories = ['content']
merge_base = %x(git merge-base origin/production HEAD).chomp
diffable_files = %x(git diff -z --name-only --diff-filter=AC #{merge_base}).split("\0")
diffable_files = diffable_files.select do |filename|
  next true if directories.include?(File.dirname(filename))

  filename.end_with?(".md")
end.map { |f| Regexp.new(File.basename(f, File.extname(f))) }

HTMLProofer.check_directory("./output", { ignore_urls: diffable_files }).run

配置

HTMLProofer 构造函数接受一个可选的附加选项哈希:

选项描述默认值
allow_hash_href如果 true,则假设 href="#" 锚点有效true
allow_missing_href如果 true,则不标记缺少 hrefa 标签。在 HTML5 中,这在技术上是允许的,但也可能是人为错误。false
assume_extension自动为内部链接的文件添加指定的扩展名,以支持无扩展名的 URL(大多数服务器均支持).html
checks一个字符串数组,指示您希望运行哪些检查['Links', 'Images', 'Scripts']
check_external_hash检查外部哈希是否存在(即使网页存在)true
check_internal_hash检查内部哈希是否存在(即使网页存在)true
check_sri检查 <link><script> 外部资源是否使用 SRIfalse
directory_index_file设置当链接指向目录时要查找的文件。(如果存在,则覆盖 directory_index_files。)index.html
directory_index_files设置当链接指向目录时要查找的文件。['index.html']
disable_external如果为 true,则不运行外部链接检查器false
enforce_https如果链接未标记为 https,则判定该链接失败。true
extensions一个字符串数组,指示您希望检查的文件扩展名(包括点号)['.html']
ignore_empty_alt如果为 true,则忽略 alt 标签为空或缺失的图片(换句话说,<img alt><img alt=""> 是有效的;将此设置为 false 以标记这些情况)true
ignore_files一个包含可安全忽略的文件路径的字符串或正则表达式数组。[]
ignore_empty_mailto如果为 true,则允许不包含电子邮件地址的 mailto: hreffalse
ignore_missing_alt如果为 true,则忽略 alt 标签缺失的图片false
ignore_status_codes一个表示要忽略的状态码的数字数组。[]
ignore_urls一个包含可安全忽略的 URL 的字符串或正则表达式数组。这会影响所有 HTML 属性,例如图片上的 alt 标签。[]
log_level设置日志级别,由 Yell 决定。为 :debug:info:warn:error:fatal 之一。:info
only_4xx仅报告状态码在 4xx 范围内的链接错误。false
root_dir提供 html 文件的目录的绝对路径。""
swap_attributes将元素名称映射到首选检查属性的 JSON 格式配置{}
swap_urls包含 RegExp => String 键值对的哈希。它通过 gsub 将匹配 RegExp 的 URL 转换为 String{}

此外,还有几个“命名空间”选项。这些是:

  • :typhoeus / :hydra
  • :cache

配置 Typhoeus 和 Hydra

Typhoeus 用于向外部 URL 发起快速、并行的请求。您可以通过 :typhoeus 的 options 命名空间,传入 Typhoeus 的任意选项以用于外部链接检查。例如:

HTMLProofer.new("out/", { extensions: [".htm"], typhoeus: { verbose: true, ssl_verifyhost: 2 } })

这将 HTMLProofer 的扩展设置为使用 .htm,为 Typhoeus 提供配置以使其输出详细信息,并使用特定的 SSL 设置。有关它可以接收的选项的更多信息,请参阅 Typhoeus 文档

类似地,你可以传入一个带有 Hydra 哈希配置的 :hydra 选项。

默认值为:

{
  typhoeus:
  {
    followlocation: true,
    connecttimeout: 10,
    timeout: 30,
  },
  hydra: { max_concurrency: 50 },
}

在 CLI 中,你可以提供 --typhoeus--hydra 参数来设置配置。这使用 JSON.parse 进行解析,并映射到默认配置值之上,以便可以覆盖它们。要在 CLI 上传入上述示例,你需要执行:

htmlproofer --typhoeus '{ "followlocation": true, "connecttimeout": 10, "timeout": 30 }' --hydra '{ "max_concurrency": 50 }'

设置 before-request 回调

您可以提供一个代码块,以在检查外部链接之前设置一些逻辑。例如,假设您希望在每次检查 GitHub URL 时提供一个身份验证令牌。您可以这样做:

proofer = HTMLProofer.check_directory(item, opts)
proofer.before_request do |request|
  request.options[:headers]["Authorization"] = "Bearer <TOKEN>" if request.base_url == "https://github.com"
end
proofer.run

当且仅当 base_urlhttps://github.com 时,才会设置 Authorization 标头,对于所有其他 URL 均予以排除。

配置缓存

检查外部 URL 可能会拖慢测试速度。如果您希望加快这一过程,可以为外部和内部链接启用缓存。缓存的含义是,对于在特定时间段内有效的链接,跳过链接检查。

您可以通过传入配置选项 :cache 来启用此功能,该选项包含一个哈希,其中有一个键 :timeframe:timeframe 定义了缓存在使用前链接被重新检查的时间长度。:timeframe 的格式是一个包含两个键的哈希,即 externalinternal。每个键都包含一个数字,后跟一个表示时间长度的字母:

  • M 表示月
  • w 表示周
  • d 表示天
  • h 表示小时

例如,传入以下选项意味着“重新检查超过三十天的外部链接”:

{ cache: { timeframe: { external: "30d" } } }

而以下选项表示“重新检查两周前的内部链接”:

{ cache: { timeframe: { internal: "2w" } } }

当然,为了同时支持内部链接和外部链接的缓存,需要提供这两个键。以下配置每两周检查一次外部链接,但每周仅检查一次内部链接:

{ cache: { timeframe: { external: "2w", internal: "1w" } } }

你还可以通过提供 storage_dir 键来更改缓存文件的文件名或存放目录:

{ cache: { cache_file: "stay_cachey.json", storage_dir: "/tmp/html-proofer-cache-money" } }

失败的链接会保留在缓存中,并_始终_重新检查。如果通过,缓存将更新以记录新的时间戳。

缓存仅针对外部链接运行。

如果启用了缓存,HTMLProofer 会写入一个名为 tmp/.htmlproofer/cache.log 的日志文件。您可能应该在版本控制系统中忽略此文件夹。

在 CLI 上,您可以提供 --cache 参数来设置配置。这使用 JSON.parse 进行解析,并映射到默认配置值之上,以便可以覆盖它们。 要在 CLI 上传入上述示例之一,您需要执行:

htmlproofer --cache '{ "timeframe": { "external": "2w", "internal": "1w" } }'

使用持续集成进行缓存

在配置 HTMLProofer 缓存其结果之后(参见上文),您可以在持续集成流程中启用缓存,以加快构建速度,并避免可能链接到的外部网站的速率限制。

在 GitHub Actions 中:

在运行 HTMLProofer 之前,将此步骤添加到您的构建工作流中:

- name: Cache HTMLProofer
  id: cache-htmlproofer
  uses: actions/cache@v2
  with:
    path: tmp/.htmlproofer
    key: ${{ runner.os }}-htmlproofer

同时确保后续运行 HTMLProofer 的步骤不会返回失败的 shell 状态。您可以尝试类似 html-proof ... || true 的操作。因为 GitHub Actions 中失败的步骤会跳过所有后续步骤。

在 Travis 中:

如果您想在 Travis CI 中启用缓存,请确保将这些行添加到您的 .travis.yml 文件中:

cache:
  directories:
    - $TRAVIS_BUILD_DIR/tmp/.htmlproofer

有关使用 HTML-Proofer 配合 Travis CI 的更多信息,请参阅此 wiki 页面

Logging

HTML-Proofer 可以尽可能吵闹或尽可能安静。如果你设置了 :log_level 选项,你可以更好地定义日志级别。

Custom tests

想编写自己的测试?当然,这是可能的!

只需创建一个继承自 HTMLProofer::Check 的类。该子类必须定义一个名为 run 的方法。它会在你的内容上被调用,并负责对你喜欢的任何元素执行验证。当你捕获到一个损坏的问题时,调用 add_failure(message, line: line, content: content) 来解释错误。line 指的是行号,而 content 是损坏元素的节点内容。

如果你正在处理元素的属性(大多数检查都是这样做的),你还希望将 create_element(node) 作为你的测试套件的一部分进行调用。这会构造一个包含你正在迭代的 HTML 元素所有属性的对象,并且也可以直接用于调用 add_failure(message, element: element)

下面是一个演示这些概念的自定义测试示例。它报告指向 octocat@github.commailto 链接:

class MailToOctocat < HTMLProofer::Check
  def mailto_octocat?
    @link.url.raw_attribute == "mailto:octocat@github.com"
  end

  def run
    @html.css("a").each do |node|
      @link = create_element(node)

      next if @link.ignore?

      return add_failure("Don't email the Octocat directly!", element: @link) if mailto_octocat?
    end
  end
end

别忘了将此新检查项添加到 HTMLProofer 的选项中,例如:

# removes default checks and just runs this one
HTMLProofer.check_directories(["out/"], { checks: ["MailToOctocat"] })

参见我们的第三方自定义类列表,并将您自己的类添加到该列表中。

报告

默认情况下,HTML-Proofer 拥有自己的报告机制,用于在运行结束时打印错误。您可以通过传入 HTMLProofer::Reporter 的自定义子类来选择使用您自己的报告器:

proofer = HTMLProofer.check_directory(item, opts)
proofer.reporter = MyCustomReporter.new(logger: proofer.logger)
proofer.run

您的自定义报告器必须实现 report 函数,以实现您希望看到的行为。logger 关键字参数是可选的。

以编程方式访问失败

运行 HTMLProofer 后,您可以通过 failed_checks 方法访问失败列表。每个失败都是一个 HTMLProofer::Failure 对象,包含有关错误的详细信息:

proofer = HTMLProofer.check_directory("./out")
proofer.run

proofer.failed_checks.each do |failure|
  puts "File: #{failure.path}"
  puts "Check: #{failure.check_name}"
  puts "Description: #{failure.description}"
  puts "Line: #{failure.line}"
  puts "Status: #{failure.status}" # HTTP status code for external links
  puts "Content: #{failure.content}" # Text content of the element
end

访问元素

每个失败还提供对原始 HTMLProofer::Element 对象的访问,该对象允许你访问底层的 Nokogiri 节点及其所有属性:

proofer.failed_checks.each do |failure|
  element = failure.element
  next if element.nil?

  # Access the Nokogiri node directly
  node = element.node
  puts "Tag name: #{node.name}"
  puts "Href: #{node['href']}"
  puts "All attributes: #{node.attributes.keys}"

  # Use helper methods
  puts "Is anchor tag: #{element.a_tag?}"
  puts "Is image tag: #{element.img_tag?}"
  puts "Link text: #{element.content}"
  puts "Line number: #{element.line}"
end

这对于构建自定义报告器、与其他工具集成或以编程方式处理验证结果非常有用。

故障排除

以下是一些简要的片段,用于识别一些你可以规避的常见问题。如需更多信息,请查阅我们的 wiki

我们的 wiki 页面关于使用 HTML-Proofer 与 Travis CI 可能也很有用。

忽略 SSL 证书

要忽略 SSL 证书,请关闭 Typhoeus 的 SSL 验证:

HTMLProofer.check_directory("out/", {
  typhoeus: {
    ssl_verifypeer: false,
    ssl_verifyhost: 0,
},
}).run

User-Agent

要更改 Typhoeus 使用的 User-Agent:

HTMLProofer.check_directory("out/", {
  typhoeus: {
    headers: { "User-Agent" => "Mozilla/5.0 (compatible; My New User-Agent)" },
  }
}).run

或者,您可以在命令行中通过以下方式指定这些选项:

htmlproofer --typhoeus='{"headers":{"User-Agent":"Mozilla/5.0 (compatible; My New User-Agent)"}}'

Cookies

有时链接会失效,因为它们无法访问 cookies。要修复此问题,您可以使用以下代码片段创建一个 .cookies 文件:

HTMLProofer.check_directory("out/", {
  typhoeus: {
    cookiefile: ".cookies",
    cookiejar: ".cookies",
  }
}).run
htmlproofer --typhoeus='{"cookiefile":".cookies","cookiejar":".cookies"}'

正则表达式

若要使用正则表达式排除 URL,请将其置于正斜杠之间,且不要加引号:

HTMLProofer.check_directories(["out/"], {
  ignore_urls: [/example.com/],
}).run

现实生活中的例子

项目仓库备注
Jekyll 的网站jekyll/jekyll一个独立脚本] 调用 htmlproofer,并且这曾经由 Circle CI 调用
Raspberry Pi 的文档raspberrypi/documentation
Squeak 的网站squeak-smalltalk/squeak.org
Atom 飞行手册atom/flight-manual.atom.io
GitHub does dotfilesdotfiles/dotfiles.github.com使用 proof-html GitHub action