Anonymous avatar Anonymous committed 011e651

Added HTML5 validation testcase.

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.

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 :::: */

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>

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

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')

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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.