Commits

Anonymous committed 011e651

Added HTML5 validation testcase.

  • Participants
  • Parent commits a8779c4

Comments (0)

Files changed (6)

 
         Hacking on Solace is fun.  It's written in Python and based
         on pupular libraries.  The dynamic parts in the user interface
-        are created using HTML 4 and jQuery.
+        are created using HTML 5 and jQuery.
 
         This file should give you a brief overview how the code works
         and what you should keep in mind when working on it.
         JavaScript code is intended with 2 spaces and only two spaces.
         The same rule applies for HTML as well.
 
-        **Every page has to validate against HTML 4.01 transitional**.
+        **Every page has to validate against HTML 5**.
 
-        If you need features HTML 4 does not give you, use JavaScript!
-	We're using the transitional DTD because recaptcha uses iframes
-	for non-JavaScript enabled browsers.
-
-        We will probably switch to HTML 5 once support is widespread
-        but until then, the doctype has to be HTML 4.01 transitional.
-	The reason for this rule is that many browser validators such as
-        the firefox validator plugin does not support HTML 5 yet which
-        makes it nearly impossible to ensure that every page validates.
+        There is a unittest that will send the pages to validator.nu
+        to check if they validate.  If you are adding a new page,
+        make sure it's somehow crawled there.
 
         CSS spacing rules should follow the existing rules in the files.
         Use whitespace after property, value colons.

File solace/static/layout.css

     font-weight: normal;
 }
 
-ul.navigation li.view_lang a big {
+ul.navigation li.view_lang a strong {
     margin-top: -2px;
     display: block;
     font-size: 13px;
-    font-weight: bold;
 }
 
 /* :::: CONTENTS :::: */

File solace/templates/layout.html

-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!doctype html>
 {%- from '_helpers.html' import render_user %}
 {%- block html_head -%}
 <title>{% block html_head_title %}{% if page_title %}{{ page_title
   <li class="view_lang">
     <a href="{{ url_for('kb.sections') }}">
       <small>{{ _('Section') }}</small>
-      <big>{{ request.view_lang.display_name }}</big>
+      <strong>{{ request.view_lang.display_name }}</strong>
     </a>
 {%- endif %}
 </ul>

File solace/tests/__init__.py

 
 def suite():
     from solace.tests import models, querycount, kb_views, core_views, \
-         templating, signals, link_check
+         templating, signals, link_check, validation
     suite = unittest.TestSuite()
     suite.addTest(models.suite())
     suite.addTest(querycount.suite())
     suite.addTest(templating.suite())
     suite.addTest(signals.suite())
     suite.addTest(link_check.suite())
+    suite.addTest(validation.suite())
     return suite

File solace/tests/validation.py

+# -*- coding: utf-8 -*-
+"""
+    solace.tests.validation
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    A unittest that validates the pages using the validator.nu HTML5
+    validator.
+
+    :copyright: (c) 2009 by Plurk Inc., see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import sys
+import unittest
+from simplejson import loads
+from urllib2 import urlopen, Request, URLError
+from urlparse import urljoin
+from solace.tests import SolaceTestCase
+
+from solace import models, settings
+from solace.database import session
+
+
+VALIDATOR_URL = 'http://html5.validator.nu/'
+BASE_URL = 'http://localhost/'
+MIN_VISITED = 12
+
+
+class ValidatorTestCase(SolaceTestCase):
+
+    def doExternalValidation(self, url, response, content_type):
+        """Do the validation."""
+        request = Request(VALIDATOR_URL + '?out=json',
+                          response, {'Content-Type': content_type})
+        response = urlopen(request)
+        body = loads(response.read())
+        response.close()
+
+        for message in body['messages']:
+            if message['type'] == 'error':
+                detail = u'on line %s [%s]\n%s' % (
+                    message['lastLine'],
+                    message['extract'],
+                    message['message']
+                )
+                self.fail((u'Got a validation error for %r:\n%s' %
+                    (url, detail)).encode('utf-8'))
+
+    def test_pages(self):
+        """Make sure that all pages are valid HTML5"""
+        settings.LANGUAGE_SECTIONS = ['en']
+        user = models.User('user1', 'user1@example.com', 'default')
+        user.active = True
+        topic = models.Topic('en', 'This is a test topic', 'Foobar', user)
+        post1 = models.Post(topic, user, 'meh1')
+        post2 = models.Post(topic, user, 'meh2')
+        topic.accept_answer(post1)
+        session.commit()
+
+        visited_links = set()
+        def visit(url):
+            url = urljoin(BASE_URL, url).split('#', 1)[0]
+            if not url.startswith(BASE_URL) or url in visited_links:
+                return
+            visited_links.add(url)
+            path = url.split('/', 3)[-1]
+            response = self.client.get(path, follow_redirects=True)
+            content_type = response.headers['Content-Type']
+            if content_type.split(';')[0].strip() == 'text/html':
+                self.doExternalValidation(url, response.data, content_type)
+            for link in response.html.xpath('//a[@href]'):
+                visit(link.attrib['href'])
+
+        self.login('user1', 'default')
+        visit('/')
+
+
+def suite():
+    suite = unittest.TestSuite()
+    # skip the test if the validator is not reachable
+    try:
+        urlopen(VALIDATOR_URL)
+    except URLError:
+        print >> sys.stderr, 'Skiping HTML5 validation tests'
+        return suite
+    suite.addTest(unittest.makeSuite(ValidatorTestCase))
+    return suite
+
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')

File solace/views/themes.py

     f = theme.open_resource(file)
     if f is None:
         raise NotFound()
-    mimetype = mimetypes.guess_type(file)[1] or 'text/plain'
-    resp = Response(wrap_file(request.environ, f), mimetype=mimetype,
+    resp = Response(wrap_file(request.environ, f),
+                    mimetype=mimetypes.guess_type(file)[0] or 'text/plain',
                     direct_passthrough=True)
     resp.add_etag()
     return resp.make_conditional(request)