rgarver (owner)

Revisions

  • 62e11b Sun Sep 07 12:30:29 -0700 2008
gist: 9300 Download_button fork
public
Public Clone URL: git://gist.github.com/9300.git
Embed All Files: show embed
be_valid_xhtml.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
# Paste me into spec_helper.rb, or save me somewhere else and require me in.
 
class BeValidXhtml
  # require 'action_controller/test_process'
  # require 'test/unit'
  require 'net/http'
  require 'md5'
  require 'ftools'
  
  def initialize
  end
  
  # Assert that markup (html/xhtml) is valid according the W3C validator web service.
  # By default, it validates the contents of @response.body, which is set after calling
  # one of the get/post/etc helper methods. You can also pass it a string to be validated.
  # Validation errors, if any, will be included in the output. The input fragment and
  # response from the validator service will be cached in the $RAILS_ROOT/tmp directory to
  # minimize network calls.
  #
  # For example, if you have a FooController with an action Bar, put this in foo_controller_test.rb:
  #
  # def test_bar_valid_markup
  # get :bar
  # assert_valid_markup
  # end
  #
  MARKUP_VALIDATOR_HOST = ENV['MARKUP_VALIDATOR_HOST'] || 'validator.w3.org'
  MARKUP_VALIDATOR_PATH = ENV['MARKUP_VALIDATOR_PATH'] || '/check'
  CSS_VALIDATOR_HOST = ENV['CSS_VALIDATOR_HOST'] || 'jigsaw.w3.org'
  CSS_VALIDATOR_PATH = ENV['CSS_VALIDATOR_PATH'] || '/css-validator/validator'
  
  @@display_invalid_content = false
  cattr_accessor :display_invalid_content
 
  @@auto_validate = false
  cattr_accessor :auto_validate
 
  class_inheritable_accessor :auto_validate_excludes
  class_inheritable_accessor :auto_validate_includes
  
  
  def matches?(response)
    return true if validity_checks_disabled?
    
    case response
    when String
      response = match_string(response)
    else
      response = match_action_controller_response(response)
    end
 
    markup_is_valid = response['x-w3c-validator-status'] == 'Valid'
    @message = ''
    unless markup_is_valid
      fragment.split($/).each_with_index{|line, index| message << "#{'%04i' % (index+1)} : #{line}#{$/}"} if @@display_invalid_content
      @message << XmlSimple.xml_in(response.body)['messages'][0]['msg'].collect{ |m| "Invalid markup: line #{m['line']}: #{CGI.unescapeHTML(m['content'])}" }.join("\n")
    end
    if markup_is_valid
      return true
    else
      return false
    end
  end
  
  def description
    "be valid xhtml"
  end
  
  def failure_message
   " expected xhtml to be valid, but validation produced these errors:\n #{@message}"
  end
  
  def negative_failure_message
    " expected to not be valid, but was (missing validation?)"
  end
  
  private
    def validity_checks_disabled?
      ENV["NONET"] == 'true'
    end
 
    def text_to_multipart(key,value)
      return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"\r\n\r\n#{value}\r\n"
    end
 
    def file_to_multipart(key,filename,mime_type,content)
      return "Content-Disposition: form-data; name=\"#{CGI::escape(key)}\"; filename=\"#{filename}\"\r\n" +
                "Content-Transfer-Encoding: binary\r\nContent-Type: #{mime_type}\r\n\r\n#{content}\r\n"
    end
 
    def cache_resource(base,resource,extension,fn)
      resource_md5 = MD5.md5(resource).to_s
      file_md5 = nil
 
      output_dir = "#{RAILS_ROOT}/tmp/#{base}"
      base_filename = File.join(output_dir, fn)
      filename = base_filename + extension
 
      parent_dir = File.dirname(filename)
      File.makedirs(parent_dir) unless File.exists?(parent_dir)
 
      File.open(filename, 'r') do |f|
        file_md5 = MD5.md5(f.read(f.stat.size)).to_s
      end if File.exists?(filename)
 
      if file_md5 != resource_md5
        Dir["#{base_filename}[^.]*"] .each {|f| File.delete(f)}
        File.open(filename, 'w+') do |f| f.write(resource); end
      end
      base_filename
    end
    
    def match_string(response)
      run_validation_on_fragment(response)
    end
    
    def match_action_controller_response(response)
      fn = response.rendered_file
      fragment = response.body
      base_filename = cache_resource('markup',fragment,'html',fn)
    
      return false unless base_filename
      results_filename = base_filename + '-results.yml'
      
      begin
        response = File.open(results_filename) do |f| Marshal.load(f) end
      rescue
        response = run_validation_on_fragment
        File.open(results_filename, 'w+') do |f| Marshal.dump(response, f) end
      end
      response
    end
    
    def run_validation_on_fragment(fragment)
      http.start(MARKUP_VALIDATOR_HOST).post2(MARKUP_VALIDATOR_PATH, "fragment=#{CGI.escape(fragment)}&output=xml")
    end
 
    def http
      if Module.constants.include?("ApplicationConfig") && ApplicationConfig.respond_to?(:proxy_config)
        Net::HTTP::Proxy(ApplicationConfig.proxy_config['host'], ApplicationConfig.proxy_config['port'])
      else
        Net::HTTP
      end
    end
  
end
 
def be_valid_xhtml
  BeValidXhtml.new
end