Skip to content

Instantly share code, notes, and snippets.

@masasakano
Created May 27, 2024 13:42
Show Gist options
  • Save masasakano/6cdfb389e8ac3e13735d2b3a306de114 to your computer and use it in GitHub Desktop.
Save masasakano/6cdfb389e8ac3e13735d2b3a306de114 to your computer and use it in GitHub Desktop.
Ruby-on-Rails helper to use a local VNU server to validate HTML with W3C validator
# -*- coding: utf-8 -*-
require 'w3c_validators'
# Module to deal with W3C Validator
#
# == Usage
#
# Put this file in one of your library-load paths in the test environment.
# Then, in /test/test_helper.rb
#
# class ActiveSupport::TestCase
# include TestW3cValidateHelper
#
# You may need to write in your +test_helper.rb+
#
# require_relative './test_w3c_validate_helper'
#
# == Preparation
#
# Make sure your Gemfile contains:
#
# group :test do
# gem 'w3c_validators'
# end
#
# To use these methods with your local VNU server (Default),
# you must install vnu first.
# See https://stackoverflow.com/a/3586244/3577922
# or for macOS Homebrew, +brew install vnu+
#
# Then, start up a local VNU server in a separate terminal with (in the case of Homebrew):
#
# java -Dnu.validator.servlet.bind-address=127.0.0.1 -cp $HOMEBREW_CELLAR/vnu/`vnu --version`/libexec/vnu.jar \
# nu.validator.servlet.Main 8888
#
# NOTE1: If +$HOMEBREW_CELLAR+ is not defined, your machine is probably Intel and it is /usr/local/Cellar
# NOTE2: The vnu default server may be http://0.0.0.0:8888/ but vnu warns it will be changed to http://127.0.0.1:8888/
# NOTE3: If the server address is different from the default, you simply give is to the methods in thi module,
# making sure you include the forward slash '/' at the tail.
module TestW3cValidateHelper
extend ActiveSupport::Concern
# Default parameters for the methods in this module.
#
# @note For +validator_uri+, the trailing "/" is indispensable.
DEF_W3C_VALIDATOR_PARAMS = {
use_local: true,
validator_uri: "http://127.0.0.1:8888/",
}.with_indifferent_access
#module ClassMethods
#end
# Validates HTML with W3C (vnu) validator
#
# If environmental variable SKIP_W3C_VALIDATE is set and not '0' or 'false',
# validation is skipped.
#
# The caller information is printed if fails.
#
# If the error message is insufficient, you may simply print out 'response.body',
# before the calling statement in the caller, or better
#
# @validator.validate_text(response.body).debug_messages.each do |key, value|
# puts "#{key}: #{value}"
# end
#
# (Note that +css_select('table').to_html+ in Controller tests etc may not work well
# for HTML-debugging purposes because it would filter out invalid HTMLs like stray end tags.)
#
# @example to call this for the output of the index method of ArticlesController
# w3c_validate "Article index"
#
# @option name [String] Identifier for the error message.
# @param use_local: #see setup_w3c_validator!
# @param validator_uri: #see setup_w3c_validator!
def w3c_validate(name="caller", use_local: nil, validator_uri: DEF_W3C_VALIDATOR_PARAMS[:validator_uri])
return if is_env_set_positive?('SKIP_W3C_VALIDATE')
bind = caller_locations(1,1)[0] # Ruby 2.0+
caller_info = sprintf "%s:%d", bind.absolute_path.sub(%r@.*(/test/)@, '\1'), bind.lineno
# NOTE: bind.label returns "block in <class:TranslationIntegrationTest>"
## W3C HTML validation (Costly operation)
setup_w3c_validator!(use_local: use_local, validator_uri: validator_uri) if !instance_variable_defined?(:@validator)
arerr = @validator.validate_text(response.body).errors
arerr = _may_ignore_autocomplete_errors_for_hidden(arerr, "Ignores W3C validation errors for #{name} (#{caller_info}): ")
assert_empty arerr, "Failed for #{name} (#{caller_info}): W3C-HTML-validation-Errors(Size=#{arerr.size}): ("+arerr.map(&:to_s).join(") (")+")"
end
# Sets up the instance variable +@validator+ for preparation for running {#w3c_validate}
#
# Note that +validator_uri+ is ignored unless +use_local+ is true (Def: false).
#
# See [TestW3cValidateHelper::DEF_W3C_VALIDATOR_PARAMS] and also the comment for this modeule
# for the default +validator_uri+
#
# If the optional argument +use_local+ is nil, the environmental variable +USE_W3C_SERVER_VALIDATOR+
# is read; then, if it is not set or set "0" or "false", +use_local+ is set true,
# and this method (attempts to) use the local VNU server. Make sure your local VNU server is
# up and running (the comment for this modeule for detail); otherwise,
# +W3CValidators::ValidatorUnavailable+ exception is raised, perhams many times.
#
# @param use_local: [Boolean, NilClass] If true (Def), use the local server. Otherwise, use the W3C server (use it sensibly!)
# @param validator_uri: [String, NilClass] read only when use_local is true (Def).
def setup_w3c_validator!(use_local: nil, validator_uri: DEF_W3C_VALIDATOR_PARAMS[:validator_uri])
use_local = !is_env_set_positive?('USE_W3C_SERVER_VALIDATOR') if use_local.nil?
hsin = {}
hsin[:validator_uri] = validator_uri if use_local
@validator = W3CValidators::NuValidator.new(**hsin)
end
if !method_defined?(:is_env_set_positive?)
# true if the environmental variable is set and non-false
def is_env_set_positive?(key)
ENV.keys.include?(key.to_s) && !(%w(0 false FALSE f F)<<"").include?(ENV[key])
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment