Moved tests out of the app to stay sane and changed a few things. E.g. the inclusion of JavaScript files now happens without charset (as they are deprecated in HTML5).

This commit is contained in:
Jannis Leidel
2011-08-10 16:29:27 +02:00
parent fdc05a64de
commit 66cced5d5b
44 changed files with 826 additions and 705 deletions

6
.gitignore vendored
View File

@@ -1,7 +1,7 @@
build
compressor/tests/media/CACHE
compressor/tests/media/custom
compressor/tests/media/js/066cd253eada.js
tests/media/CACHE
tests/media/custom
tests/media/js/066cd253eada.js
dist
MANIFEST
*.pyc

View File

@@ -2,4 +2,4 @@ include AUTHORS
include README.rst
include LICENSE
recursive-include compressor/templates/compressor *.html
recursive-include compressor/tests/media *.js *.css *.png
recursive-include tests *.js *.css *.png *.py

View File

@@ -1,4 +1,6 @@
from __future__ import with_statement
import os
import codecs
from django.core.files.base import ContentFile
from django.template.loader import render_to_string
@@ -10,10 +12,10 @@ from compressor.exceptions import CompressorError, UncompressableFileError
from compressor.filters import CompilerFilter
from compressor.storage import default_storage
from compressor.utils import get_class, staticfiles
from compressor.utils.decorators import cached_property
from compressor.utils.decorators import cached_property, memoize
# Some constants for nicer handling.
SOURCE_HUNK, SOURCE_FILE = 1, 2
SOURCE_HUNK, SOURCE_FILE = 'inline', 'file'
METHOD_INPUT, METHOD_OUTPUT = 'input', 'output'
@@ -27,6 +29,7 @@ class Compressor(object):
def __init__(self, content=None, output_prefix="compressed"):
self.content = content or ""
self.output_prefix = output_prefix
self.output_dir = settings.COMPRESS_OUTPUT_DIR.strip('/')
self.charset = settings.DEFAULT_CHARSET
self.storage = default_storage
self.split_content = []
@@ -47,17 +50,21 @@ class Compressor(object):
except AttributeError:
base_url = settings.COMPRESS_URL
if not url.startswith(base_url):
raise UncompressableFileError(
"'%s' isn't accesible via COMPRESS_URL ('%s') and can't be"
" compressed" % (url, base_url))
raise UncompressableFileError("'%s' isn't accesible via "
"COMPRESS_URL ('%s') and can't be "
"compressed" % (url, base_url))
basename = url.replace(base_url, "", 1)
# drop the querystring, which is used for non-compressed cache-busting.
return basename.split("?", 1)[0]
def get_filepath(self, content):
filename = "%s.%s" % (get_hexdigest(content, 12), self.type)
return os.path.join(self.output_dir, self.output_prefix, filename)
def get_filename(self, basename):
# first try to find it with staticfiles (in debug mode)
filename = None
if settings.DEBUG and self.finders:
if self.finders:
filename = self.finders.find(basename)
# secondly try finding the file in the root
elif self.storage.exists(basename):
@@ -66,9 +73,17 @@ class Compressor(object):
return filename
# or just raise an exception as the last resort
raise UncompressableFileError(
"'%s' could not be found in the COMPRESS_ROOT '%s'%s" % (
basename, settings.COMPRESS_ROOT,
self.finders and " or with staticfiles." or "."))
"'%s' could not be found in the COMPRESS_ROOT '%s'%s" %
(basename, settings.COMPRESS_ROOT,
self.finders and " or with staticfiles." or "."))
def get_filecontent(self, filename, charset):
with codecs.open(filename, 'rb', charset) as fd:
try:
return fd.read()
except IOError, e:
raise UncompressableFileError("IOError while processing "
"'%s': %s" % (filename, e))
@cached_property
def parser(self):
@@ -89,36 +104,70 @@ class Compressor(object):
return get_hexdigest(''.join(
[self.content] + self.mtimes).encode(self.charset), 12)
@cached_property
def hunks(self):
for kind, value, basename, elem in self.split_contents():
if kind == SOURCE_HUNK:
content = self.filter(value, METHOD_INPUT,
elem=elem, kind=kind, basename=basename)
yield smart_unicode(content)
elif kind == SOURCE_FILE:
content = ""
fd = open(value, 'rb')
try:
content = fd.read()
except IOError, e:
raise UncompressableFileError(
"IOError while processing '%s': %s" % (value, e))
finally:
fd.close()
content = self.filter(content, METHOD_INPUT,
filename=value, basename=basename, elem=elem, kind=kind)
attribs = self.parser.elem_attribs(elem)
charset = attribs.get("charset", self.charset)
yield smart_unicode(content, charset.lower())
@memoize
def hunks(self, mode='file'):
"""
The heart of content parsing, iterates of the
list of split contents and looks at its kind
to decide what to do with it. Should yield a
bunch of precompiled and/or rendered hunks.
"""
enabled = settings.COMPRESS_ENABLED
@cached_property
def concat(self):
return '\n'.join((hunk.encode(self.charset) for hunk in self.hunks))
for kind, value, basename, elem in self.split_contents():
precompiled = False
attribs = self.parser.elem_attribs(elem)
charset = attribs.get("charset", self.charset)
options = {
'method': METHOD_INPUT,
'elem': elem,
'kind': kind,
'basename': basename,
}
if kind == SOURCE_FILE:
options = dict(options, filename=value)
value = self.get_filecontent(value, charset)
if self.all_mimetypes:
precompiled, value = self.precompile(value, **options)
if enabled:
value = self.filter(value, **options)
yield mode, smart_unicode(value, charset.lower())
else:
if precompiled:
value = self.handle_output(kind, value, forced=True)
yield "verbatim", smart_unicode(value, charset.lower())
else:
yield mode, self.parser.elem_str(elem)
@memoize
def filtered_output(self, content):
"""
Passes the concatenated content to the 'output' methods
of the compressor filters.
"""
return self.filter(content, method=METHOD_OUTPUT)
@memoize
def filtered_input(self, mode='file'):
"""
Passes each hunk (file or code) to the 'input' methods
of the compressor filters.
"""
verbatim_content = []
rendered_content = []
for mode, hunk in self.hunks(mode):
if mode == 'verbatim':
verbatim_content.append(hunk)
else:
rendered_content.append(hunk)
return verbatim_content, rendered_content
def precompile(self, content, kind=None, elem=None, filename=None, **kwargs):
if not kind:
return content
return False, content
attrs = self.parser.elem_attribs(elem)
mimetype = attrs.get("type", None)
if mimetype:
@@ -129,15 +178,11 @@ class Compressor(object):
"COMPRESS_PRECOMPILERS setting for "
"mimetype '%s'." % mimetype)
else:
return CompilerFilter(content, filter_type=self.type,
return True, CompilerFilter(content, filter_type=self.type,
command=command, filename=filename).input(**kwargs)
return content
return False, content
def filter(self, content, method, **kwargs):
# run compiler
if method == METHOD_INPUT:
content = self.precompile(content, **kwargs)
for filter_cls in self.cached_filters:
filter_func = getattr(
filter_cls(content, filter_type=self.type), method)
@@ -148,34 +193,28 @@ class Compressor(object):
pass
return content
@cached_property
def combined(self):
return self.filter(self.concat, method=METHOD_OUTPUT)
def filepath(self, content):
return os.path.join(settings.COMPRESS_OUTPUT_DIR.strip(os.sep),
self.output_prefix, "%s.%s" % (get_hexdigest(content, 12), self.type))
def output(self, mode='file', forced=False):
"""
The general output method, override in subclass if you need to do
any custom modification. Calls other mode specific methods or simply
returns the content directly.
"""
# First check whether we should do the full compression,
# including precompilation (or if it's forced)
if settings.COMPRESS_ENABLED or forced:
content = self.combined
elif settings.COMPRESS_PRECOMPILERS:
# or concatting it, if pre-compilation is enabled
content = self.concat
else:
# or just doing nothing, when neither
# compression nor compilation is enabled
return self.content
# Shortcurcuit in case the content is empty.
if not content:
verbatim_content, rendered_content = self.filtered_input(mode)
if not verbatim_content and not rendered_content:
return ''
if settings.COMPRESS_ENABLED or forced:
filtered_content = self.filtered_output(
'\n'.join((c.encode(self.charset) for c in rendered_content)))
finished_content = self.handle_output(mode, filtered_content, forced)
verbatim_content.append(finished_content)
if verbatim_content:
return '\n'.join(verbatim_content)
return self.content
def handle_output(self, mode, content, forced):
# Then check for the appropriate output method and call it
output_func = getattr(self, "output_%s" % mode, None)
if callable(output_func):
@@ -189,7 +228,7 @@ class Compressor(object):
The output method that saves the content to a file and renders
the appropriate template with the file's URL.
"""
new_filepath = self.filepath(content)
new_filepath = self.get_filepath(content)
if not self.storage.exists(new_filepath) or forced:
self.storage.save(new_filepath, ContentFile(content))
url = self.storage.url(new_filepath)

View File

@@ -10,7 +10,7 @@ class CssCompressor(Compressor):
def __init__(self, content=None, output_prefix="css"):
super(CssCompressor, self).__init__(content, output_prefix)
self.filters = list(settings.COMPRESS_CSS_FILTERS)
self.type = 'css'
self.type = output_prefix
def split_contents(self):
if self.split_content:
@@ -21,13 +21,9 @@ class CssCompressor(Compressor):
elem_name = self.parser.elem_name(elem)
elem_attribs = self.parser.elem_attribs(elem)
if elem_name == 'link' and elem_attribs['rel'] == 'stylesheet':
try:
basename = self.get_basename(elem_attribs['href'])
filename = self.get_filename(basename)
data = (SOURCE_FILE, filename, basename, elem)
except UncompressableFileError:
if settings.DEBUG:
raise
basename = self.get_basename(elem_attribs['href'])
filename = self.get_filename(basename)
data = (SOURCE_FILE, filename, basename, elem)
elif elem_name == 'style':
data = (SOURCE_HUNK, self.parser.elem_content(elem), None, elem)
if data:
@@ -38,15 +34,15 @@ class CssCompressor(Compressor):
if self.media_nodes and self.media_nodes[-1][0] == media:
self.media_nodes[-1][1].split_content.append(data)
else:
node = CssCompressor(str(elem))
node = CssCompressor(self.parser.elem_str(elem))
node.split_content.append(data)
self.media_nodes.append((media, node))
return self.split_content
def output(self, *args, **kwargs):
# Populate self.split_content
if (settings.COMPRESS_ENABLED or settings.COMPRESS_PRECOMPILERS or
kwargs.get('forced', False)):
# Populate self.split_content
self.split_contents()
if hasattr(self, 'media_nodes'):
ret = []

View File

@@ -10,7 +10,7 @@ class JsCompressor(Compressor):
def __init__(self, content=None, output_prefix="js"):
super(JsCompressor, self).__init__(content, output_prefix)
self.filters = list(settings.COMPRESS_JS_FILTERS)
self.type = 'js'
self.type = output_prefix
def split_contents(self):
if self.split_content:
@@ -18,14 +18,10 @@ class JsCompressor(Compressor):
for elem in self.parser.js_elems():
attribs = self.parser.elem_attribs(elem)
if 'src' in attribs:
try:
basename = self.get_basename(attribs['src'])
filename = self.get_filename(basename)
self.split_content.append(
(SOURCE_FILE, filename, basename, elem))
except UncompressableFileError:
if settings.DEBUG:
raise
basename = self.get_basename(attribs['src'])
filename = self.get_filename(basename)
content = (SOURCE_FILE, filename, basename, elem)
self.split_content.append(content)
else:
content = self.parser.elem_content(elem)
self.split_content.append((SOURCE_HUNK, content, None, elem))

View File

@@ -1 +1 @@
<style type="text/css"{% if media %} media="{{ media }}"{% endif %}>{{ content|safe }}</style>
<style type="text/css"{% if media %} media="{{ media }}"{% endif %}>{{ content|safe }}</style>

View File

@@ -1 +1 @@
<script type="text/javascript" src="{{ url }}" charset="utf-8"></script>
<script type="text/javascript" src="{{ url }}"></script>

View File

@@ -1,609 +0,0 @@
from __future__ import with_statement
import os
import re
import sys
from unittest2 import skipIf
from BeautifulSoup import BeautifulSoup
try:
import lxml
except ImportError:
lxml = None
try:
import html5lib
except ImportError:
html5lib = None
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
BeautifulSoup = None
from django.core.cache.backends import locmem
from django.core.files.storage import get_storage_class
from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase
from compressor import base
from compressor.base import SOURCE_HUNK, SOURCE_FILE
from compressor.cache import get_hashed_mtime, get_hexdigest
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.js import JsCompressor
from compressor.management.commands.compress import Command as CompressCommand
from compressor.utils import find_command
from compressor.filters.base import CompilerFilter
def css_tag(href, **kwargs):
rendered_attrs = ''.join(['%s="%s" ' % (k, v) for k, v in kwargs.items()])
template = u'<link rel="stylesheet" href="%s" type="text/css" %s/>'
return template % (href, rendered_attrs)
here = os.path.abspath(os.path.dirname(__file__))
class CompressorTestCase(TestCase):
def setUp(self):
self.maxDiff = None
settings.COMPRESS_ENABLED = True
settings.COMPRESS_PRECOMPILERS = {}
settings.COMPRESS_DEBUG_TOGGLE = 'nocompress'
self.css = """
<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid green;}</style>
<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8">
"""
self.css_node = CssCompressor(self.css)
self.js = """
<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
"""
self.js_node = JsCompressor(self.js)
def test_css_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" charset="utf-8" />'),
(SOURCE_HUNK, u'p { border:5px solid green;}', None, u'<style type="text/css">p { border:5px solid green;}</style>'),
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8" />'),
]
split = self.css_node.split_contents()
split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_css_hunks(self):
out = ['body { background:#990; }', u'p { border:5px solid green;}', 'body { color:#fff; }']
self.assertEqual(out, list(self.css_node.hunks))
def test_css_output(self):
out = u'body { background:#990; }\np { border:5px solid green;}\nbody { color:#fff; }'
self.assertEqual(out, self.css_node.combined)
def test_css_mtimes(self):
is_date = re.compile(r'^\d{10}[\.\d]+$')
for date in self.css_node.mtimes:
self.assertTrue(is_date.match(str(float(date))),
"mtimes is returning something that doesn't look like a date: %s" % date)
def test_css_return_if_off(self):
settings.COMPRESS_ENABLED = False
self.assertEqual(self.css, self.css_node.output())
def test_cachekey(self):
is_cachekey = re.compile(r'\w{12}')
self.assertTrue(is_cachekey.match(self.css_node.cachekey),
"cachekey is returning something that doesn't look like r'\w{12}'")
def test_css_hash(self):
self.assertEqual('c618e6846d04', get_hexdigest(self.css, 12))
def test_css_return_if_on(self):
output = css_tag('/media/CACHE/css/e41ba2cc6982.css')
self.assertEqual(output, self.css_node.output().strip())
def test_js_split(self):
out = [(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', '<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>'),
(SOURCE_HUNK, u'obj.value = "value";', None, '<script type="text/javascript" charset="utf-8">obj.value = "value";</script>')
]
split = self.js_node.split_contents()
split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_js_hunks(self):
out = ['obj = {};', u'obj.value = "value";']
self.assertEqual(out, list(self.js_node.hunks))
def test_js_concat(self):
out = u'obj = {};\nobj.value = "value";'
self.assertEqual(out, self.js_node.concat)
def test_js_output(self):
out = u'obj={};obj.value="value";'
self.assertEqual(out, self.js_node.combined)
def test_js_return_if_off(self):
try:
enabled = settings.COMPRESS_ENABLED
precompilers = settings.COMPRESS_PRECOMPILERS
settings.COMPRESS_ENABLED = False
settings.COMPRESS_PRECOMPILERS = {}
self.assertEqual(self.js, self.js_node.output())
finally:
settings.COMPRESS_ENABLED = enabled
settings.COMPRESS_PRECOMPILERS = precompilers
def test_js_return_if_on(self):
output = u'<script type="text/javascript" src="/media/CACHE/js/066cd253eada.js" charset="utf-8"></script>'
self.assertEqual(output, self.js_node.output())
def test_custom_output_dir(self):
try:
old_output_dir = settings.COMPRESS_OUTPUT_DIR
settings.COMPRESS_OUTPUT_DIR = 'custom'
output = u'<script type="text/javascript" src="/media/custom/js/066cd253eada.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.COMPRESS_OUTPUT_DIR = ''
output = u'<script type="text/javascript" src="/media/js/066cd253eada.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.COMPRESS_OUTPUT_DIR = '/custom/nested/'
output = u'<script type="text/javascript" src="/media/custom/nested/js/066cd253eada.js" charset="utf-8"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
finally:
settings.COMPRESS_OUTPUT_DIR = old_output_dir
class ParserTestCase(object):
def setUp(self):
self.old_parser = settings.COMPRESS_PARSER
settings.COMPRESS_PARSER = self.parser_cls
super(ParserTestCase, self).setUp()
def tearDown(self):
settings.COMPRESS_PARSER = self.old_parser
class LxmlParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.LxmlParser'
LxmlParserTests = skipIf(lxml is None, 'lxml not found')(LxmlParserTests)
class Html5LibParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.Html5LibParser'
def test_css_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u'<link charset="utf-8" href="/media/css/one.css" rel="stylesheet" type="text/css">'),
(SOURCE_HUNK, u'p { border:5px solid green;}', None, u'<style type="text/css">p { border:5px solid green;}</style>'),
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u'<link charset="utf-8" href="/media/css/two.css" rel="stylesheet" type="text/css">'),
]
split = self.css_node.split_contents()
split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_js_split(self):
out = [(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', u'<script charset="utf-8" src="/media/js/one.js" type="text/javascript"></script>'),
(SOURCE_HUNK, u'obj.value = "value";', None, u'<script charset="utf-8" type="text/javascript">obj.value = "value";</script>')
]
split = self.js_node.split_contents()
split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
Html5LibParserTests = skipIf(
html5lib is None, 'html5lib not found')(Html5LibParserTests)
class BeautifulSoupParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.BeautifulSoupParser'
BeautifulSoupParserTests = skipIf(
BeautifulSoup is None, 'BeautifulSoup not found')(BeautifulSoupParserTests)
class HtmlParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.HtmlParser'
class CssAbsolutizingTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/url/url1.css" type="text/css" charset="utf-8">
<link rel="stylesheet" href="/media/css/url/2/url2.css" type="text/css" charset="utf-8">
"""
self.css_node = CssCompressor(self.css)
def test_css_absolute_filter(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'http://media.example.com/'
filter = CssAbsoluteFilter(content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_https(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_relative_path(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_hunks(self):
hash_dict = {
'hash1': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/url1.css')),
'hash2': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/2/url2.css')),
}
out = [u"p { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\n" % hash_dict,
u"p { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\n" % hash_dict]
self.assertEqual(out, list(self.css_node.hunks))
class CssDataUriTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.datauri.CssDataUriFilter',
]
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/datauri.css" type="text/css" charset="utf-8">
"""
self.css_node = CssCompressor(self.css)
def test_data_uris(self):
datauri_hash = get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/datauri.css'))
out = [u'.add { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg=="); }\n.python { background-image: url("/media/img/python.png?%s"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n' % datauri_hash]
self.assertEqual(out, list(self.css_node.hunks))
class CssMediaTestCase(TestCase):
def setUp(self):
self.css = """
<link rel="stylesheet" href="/media/css/one.css" type="text/css" media="screen" charset="utf-8">
<style type="text/css" media="print">p { border:5px solid green;}</style>
<link rel="stylesheet" href="/media/css/two.css" type="text/css" charset="utf-8" media="all">
<style type="text/css">h1 { border:5px solid green;}</style>
"""
self.css_node = CssCompressor(self.css)
def test_css_output(self):
links = BeautifulSoup(self.css_node.output()).findAll('link')
media = [u'screen', u'print', u'all', None]
self.assertEqual(len(links), 4)
self.assertEqual(media, [l.get('media', None) for l in links])
def test_avoid_reordering_css(self):
css = self.css + '<style type="text/css" media="print">p { border:10px solid red;}</style>'
node = CssCompressor(css)
media = [u'screen', u'print', u'all', None, u'print']
links = BeautifulSoup(node.output()).findAll('link')
self.assertEqual(media, [l.get('media', None) for l in links])
class CssMinTestCase(TestCase):
def test_cssmin_filter(self):
from compressor.filters.cssmin import CSSMinFilter
content = """p {
background: rgb(51,102,153) url('../../images/image.gif');
}
"""
output = "p{background:#369 url('../../images/image.gif')}"
self.assertEqual(output, CSSMinFilter(content).output())
def render(template_string, context_dict=None):
"""A shortcut for testing template output."""
if context_dict is None:
context_dict = {}
c = Context(context_dict)
t = Template(template_string)
return t.render(c).strip()
class TemplatetagTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
def test_empty_tag(self):
template = u"""{% load compress %}{% compress js %}{% block js %}
{% endblock %}{% endcompress %}"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
self.assertEqual(u'', render(template, context))
def test_css_tag(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/one.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid green;}</style>
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css" charset="utf-8">
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = css_tag("/media/CACHE/css/e41ba2cc6982.css")
self.assertEqual(out, render(template, context))
def test_nonascii_css_tag(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/nonasc.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid green;}</style>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = css_tag("/media/CACHE/css/799f6defe43c.css")
self.assertEqual(out, render(template, context))
def test_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/one.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/CACHE/js/066cd253eada.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
def test_nonascii_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/nonasc.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">var test_value = "\u2014";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/CACHE/js/e214fe629b28.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
def test_nonascii_latin1_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/nonasc_latin1.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">var test_value = "\u2014";</script>
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = u'<script type="text/javascript" src="/media/CACHE/js/f1be5a5de243.js" charset="utf-8"></script>'
self.assertEqual(out, render(template, context))
def test_compress_tag_with_illegal_arguments(self):
template = u"""{% load compress %}{% compress pony %}
<script type="pony/application">unicorn</script>
{% endcompress %}"""
self.assertRaises(TemplateSyntaxError, render, template, {})
def test_debug_toggle(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/one.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
{% endcompress %}
"""
class MockDebugRequest(object):
GET = {settings.COMPRESS_DEBUG_TOGGLE: 'true'}
context = { 'MEDIA_URL': settings.COMPRESS_URL, 'request': MockDebugRequest()}
out = u"""<script src="/media/js/one.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>"""
self.assertEqual(out, render(template, context))
class StorageTestCase(TestCase):
def setUp(self):
self._storage = base.default_storage
base.default_storage = get_storage_class('compressor.storage.GzipCompressorFileStorage')()
settings.COMPRESS_ENABLED = True
def tearDown(self):
base.default_storage = self._storage
def test_css_tag_with_storage(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/one.css" type="text/css" charset="utf-8">
<style type="text/css">p { border:5px solid white;}</style>
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css" charset="utf-8">
{% endcompress %}
"""
context = { 'MEDIA_URL': settings.COMPRESS_URL }
out = css_tag("/media/CACHE/css/1d4424458f88.css")
self.assertEqual(out, render(template, context))
class VerboseTestCase(CompressorTestCase):
def setUp(self):
super(VerboseTestCase, self).setUp()
settings.COMPRESS_VERBOSE = True
class CacheBackendTestCase(CompressorTestCase):
def test_correct_backend(self):
from compressor.cache import cache
self.assertEqual(cache.__class__, locmem.CacheClass)
class OfflineGenerationTestCase(TestCase):
"""Uses templates/test_compressor_offline.html"""
maxDiff = None
def setUp(self):
self._old_compress = settings.COMPRESS_ENABLED
self._old_compress_offline = settings.COMPRESS_OFFLINE
settings.COMPRESS_ENABLED = True
settings.COMPRESS_OFFLINE = True
self.template_file = open(os.path.join(here, "templates/test_compressor_offline.html"))
self.template = Template(self.template_file.read().decode(settings.FILE_CHARSET))
def tearDown(self):
settings.COMPRESS_ENABLED = self._old_compress
settings.COMPRESS_OFFLINE = self._old_compress_offline
self.template_file.close()
def test_offline(self):
count, result = CompressCommand().compress()
self.assertEqual(5, count)
self.assertEqual([
css_tag('/media/CACHE/css/cd579b7deb7d.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js" charset="utf-8"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/67ed6aff7f7b.css" type="text/css" />\n',
], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves?
rendered_template = self.template.render(Context({})).replace("\n", "")
self.assertEqual(rendered_template, "".join(result).replace("\n", ""))
def test_offline_with_context(self):
self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT
settings.COMPRESS_OFFLINE_CONTEXT = {
'color': 'blue',
}
count, result = CompressCommand().compress()
self.assertEqual(5, count)
self.assertEqual([
css_tag('/media/CACHE/css/ee62fbfd116a.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js" charset="utf-8"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js" charset="utf-8"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/73e015f740c6.css" type="text/css" />\n',
], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves?
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT)).replace("\n", "")
self.assertEqual(rendered_template, "".join(result).replace("\n", ""))
settings.COMPRESS_OFFLINE_CONTEXT = self._old_offline_context
def test_get_loaders(self):
old_loaders = settings.TEMPLATE_LOADERS
settings.TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
try:
from django.template.loaders.filesystem import Loader as FileSystemLoader
from django.template.loaders.app_directories import Loader as AppDirectoriesLoader
except ImportError:
pass
else:
loaders = CompressCommand().get_loaders()
self.assertTrue(isinstance(loaders[0], FileSystemLoader))
self.assertTrue(isinstance(loaders[1], AppDirectoriesLoader))
finally:
settings.TEMPLATE_LOADERS = old_loaders
class CssTidyTestCase(TestCase):
def test_tidy(self):
content = """
/* Some comment */
font,th,td,p{
color: black;
}
"""
from compressor.filters.csstidy import CSSTidyFilter
self.assertEqual(
"font,th,td,p{color:#000;}", CSSTidyFilter(content).input())
CssTidyTestCase = skipIf(
find_command(settings.COMPRESS_CSSTIDY_BINARY) is None,
'CSStidy binary %r not found' % settings.COMPRESS_CSSTIDY_BINARY
)(CssTidyTestCase)
class CompassTestCase(TestCase):
def setUp(self):
self.old_debug = settings.DEBUG
self.old_compress_css_filters = settings.COMPRESS_CSS_FILTERS
self.old_compress_url = settings.COMPRESS_URL
self.old_enabled = settings.COMPRESS_ENABLED
settings.DEBUG = True
settings.COMPRESS_ENABLED = True
settings.COMPRESS_CSS_FILTERS = [
'compressor.filters.compass.CompassFilter',
'compressor.filters.css_default.CssAbsoluteFilter',
]
settings.COMPRESS_URL = '/media/'
def tearDown(self):
settings.DEBUG = self.old_debug
settings.COMPRESS_URL = self.old_compress_url
settings.COMPRESS_ENABLED = self.old_enabled
settings.COMPRESS_CSS_FILTERS = self.old_compress_css_filters
def test_compass(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/screen.scss" type="text/css" charset="utf-8">
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/print.scss" type="text/css" charset="utf-8">
{% endcompress %}
"""
context = {'MEDIA_URL': settings.COMPRESS_URL}
out = css_tag("/media/CACHE/css/8ff1cfd8787d.css")
self.assertEqual(out, render(template, context))
CompassTestCase = skipIf(
find_command(settings.COMPRESS_COMPASS_BINARY) is None,
'Compass binary %r not found' % settings.COMPRESS_COMPASS_BINARY
)(CompassTestCase)
class PrecompilerTestCase(TestCase):
def setUp(self):
self.this_dir = os.path.dirname(__file__)
self.filename = os.path.join(self.this_dir, 'media/css/one.css')
with open(self.filename) as f:
self.content = f.read()
self.test_precompiler = os.path.join(self.this_dir, 'precompiler.py')
def test_precompiler_infile_outfile(self):
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_infile_stdout(self):
command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())
def test_precompiler_stdin_outfile(self):
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_stdin_stdout(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())
def test_precompiler_stdin_stdout_filename(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())

View File

@@ -1,3 +1,29 @@
import functools
class memoize(object):
def __init__ (self, func):
self.func = func
def __call__ (self, *args, **kwargs):
if (args, str(kwargs)) in self.__dict__:
value = self.__dict__[args, str(kwargs)]
else:
value = self.func(*args, **kwargs)
self.__dict__[args, str(kwargs)] = value
return value
def __repr__(self):
"""
Return the function's docstring.
"""
return self.func.__doc__ or ''
def __get__(self, obj, objtype):
"""
Support instance methods.
"""
return functools.partial(self.__call__, obj)
class cached_property(object):

View File

@@ -109,7 +109,7 @@ setup(
long_description = README,
author = 'Jannis Leidel',
author_email = 'jannis@leidel.info',
packages = find_packages(),
packages = find_packages(exclude=['tests', 'tests.*']),
package_data = find_package_data('compressor', only_in_packages=False),
classifiers = [
'Development Status :: 4 - Beta',

View File

Before

Width:  |  Height:  |  Size: 733 B

After

Width:  |  Height:  |  Size: 733 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -14,7 +14,7 @@ if not settings.configured:
DATABASE_ENGINE='sqlite3',
INSTALLED_APPS=[
'compressor',
'compressor.tests',
'tests',
],
MEDIA_URL = '/media/',
MEDIA_ROOT = os.path.join(TEST_DIR, 'media'),
@@ -30,14 +30,13 @@ from django.test.simple import run_tests
def runtests(*test_args):
if not test_args:
test_args = ['tests']
parent_dir = os.path.join(TEST_DIR, "..", "..")
parent_dir = os.path.join(TEST_DIR, "..")
sys.path.insert(0, parent_dir)
cov = coverage.coverage(branch=True,
include=[
os.path.join(parent_dir, 'compressor', '*.py')
],
omit=[
join(parent_dir, 'compressor', 'tests', '*.py'),
join(parent_dir, 'compressor', 'utils', 'stringformat.py'),
join(parent_dir, 'compressor', 'filters', 'jsmin', 'rjsmin.py'),
join(parent_dir, 'compressor', 'filters', 'cssmin', 'cssmin.py'),

6
tests/tests/__init__.py Normal file
View File

@@ -0,0 +1,6 @@
from .base import CompressorTestCase, CssMediaTestCase, VerboseTestCase, CacheBackendTestCase
from .filters import CssTidyTestCase, CompassTestCase, PrecompilerTestCase, CssMinTestCase, CssAbsolutizingTestCase, CssDataUriTestCase
from .offline import OfflineGenerationTestCase
from .parsers import LxmlParserTests, Html5LibParserTests, BeautifulSoupParserTests, HtmlParserTests
from .storages import StorageTestCase
from .templatetags import TemplatetagTestCase

169
tests/tests/base.py Normal file
View File

@@ -0,0 +1,169 @@
from __future__ import with_statement
import os
import re
from BeautifulSoup import BeautifulSoup
from django.core.cache.backends import locmem
from django.test import TestCase
from compressor.base import SOURCE_HUNK, SOURCE_FILE
from compressor.cache import get_hexdigest
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.js import JsCompressor
def css_tag(href, **kwargs):
rendered_attrs = ''.join(['%s="%s" ' % (k, v) for k, v in kwargs.items()])
template = u'<link rel="stylesheet" href="%s" type="text/css" %s/>'
return template % (href, rendered_attrs)
test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
class CompressorTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_PRECOMPILERS = {}
settings.COMPRESS_DEBUG_TOGGLE = 'nocompress'
self.css = """\
<link rel="stylesheet" href="/media/css/one.css" type="text/css" />
<style type="text/css">p { border:5px solid green;}</style>
<link rel="stylesheet" href="/media/css/two.css" type="text/css" />"""
self.css_node = CssCompressor(self.css)
self.js = """\
<script src="/media/js/one.js" type="text/javascript"></script>
<script type="text/javascript">obj.value = "value";</script>"""
self.js_node = JsCompressor(self.js)
def test_css_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u'<link rel="stylesheet" href="/media/css/one.css" type="text/css" />'),
(SOURCE_HUNK, u'p { border:5px solid green;}', None, u'<style type="text/css">p { border:5px solid green;}</style>'),
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u'<link rel="stylesheet" href="/media/css/two.css" type="text/css" />'),
]
split = self.css_node.split_contents()
split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_css_hunks(self):
out = ['body { background:#990; }', u'p { border:5px solid green;}', 'body { color:#fff; }']
hunks = [h for m, h in self.css_node.hunks()]
self.assertEqual(out, hunks)
def test_css_output(self):
out = u'body { background:#990; }\np { border:5px solid green;}\nbody { color:#fff; }'
hunks = '\n'.join([h for m, h in self.css_node.hunks()])
self.assertEqual(out, hunks)
def test_css_mtimes(self):
is_date = re.compile(r'^\d{10}[\.\d]+$')
for date in self.css_node.mtimes:
self.assertTrue(is_date.match(str(float(date))),
"mtimes is returning something that doesn't look like a date: %s" % date)
def test_css_return_if_off(self):
settings.COMPRESS_ENABLED = False
self.assertEqual(self.css, self.css_node.output())
def test_cachekey(self):
is_cachekey = re.compile(r'\w{12}')
self.assertTrue(is_cachekey.match(self.css_node.cachekey),
"cachekey is returning something that doesn't look like r'\w{12}'")
def test_css_return_if_on(self):
output = css_tag('/media/CACHE/css/e41ba2cc6982.css')
self.assertEqual(output, self.css_node.output().strip())
def test_js_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', '<script src="/media/js/one.js" type="text/javascript"></script>'),
(SOURCE_HUNK, u'obj.value = "value";', None, '<script type="text/javascript">obj.value = "value";</script>')
]
split = self.js_node.split_contents()
split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_js_hunks(self):
out = ['obj = {};', u'obj.value = "value";']
hunks = [h for m, h in self.js_node.hunks()]
self.assertEqual(out, hunks)
def test_js_output(self):
out = u'<script type="text/javascript" src="/media/CACHE/js/066cd253eada.js"></script>'
self.assertEqual(out, self.js_node.output())
def test_js_return_if_off(self):
try:
enabled = settings.COMPRESS_ENABLED
precompilers = settings.COMPRESS_PRECOMPILERS
settings.COMPRESS_ENABLED = False
settings.COMPRESS_PRECOMPILERS = {}
self.assertEqual(self.js, self.js_node.output())
finally:
settings.COMPRESS_ENABLED = enabled
settings.COMPRESS_PRECOMPILERS = precompilers
def test_js_return_if_on(self):
output = u'<script type="text/javascript" src="/media/CACHE/js/066cd253eada.js"></script>'
self.assertEqual(output, self.js_node.output())
def test_custom_output_dir(self):
try:
old_output_dir = settings.COMPRESS_OUTPUT_DIR
settings.COMPRESS_OUTPUT_DIR = 'custom'
output = u'<script type="text/javascript" src="/media/custom/js/066cd253eada.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.COMPRESS_OUTPUT_DIR = ''
output = u'<script type="text/javascript" src="/media/js/066cd253eada.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
settings.COMPRESS_OUTPUT_DIR = '/custom/nested/'
output = u'<script type="text/javascript" src="/media/custom/nested/js/066cd253eada.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
finally:
settings.COMPRESS_OUTPUT_DIR = old_output_dir
class CssMediaTestCase(TestCase):
def setUp(self):
self.css = """\
<link rel="stylesheet" href="/media/css/one.css" type="text/css" media="screen">
<style type="text/css" media="print">p { border:5px solid green;}</style>
<link rel="stylesheet" href="/media/css/two.css" type="text/css" media="all">
<style type="text/css">h1 { border:5px solid green;}</style>"""
self.css_node = CssCompressor(self.css)
def test_css_output(self):
links = BeautifulSoup(self.css_node.output()).findAll('link')
media = [u'screen', u'print', u'all', None]
self.assertEqual(len(links), 4)
self.assertEqual(media, [l.get('media', None) for l in links])
def test_avoid_reordering_css(self):
css = self.css + '<style type="text/css" media="print">p { border:10px solid red;}</style>'
node = CssCompressor(css)
media = [u'screen', u'print', u'all', None, u'print']
links = BeautifulSoup(node.output()).findAll('link')
self.assertEqual(media, [l.get('media', None) for l in links])
class VerboseTestCase(CompressorTestCase):
def setUp(self):
super(VerboseTestCase, self).setUp()
settings.COMPRESS_VERBOSE = True
class CacheBackendTestCase(CompressorTestCase):
def test_correct_backend(self):
from compressor.cache import cache
self.assertEqual(cache.__class__, locmem.CacheClass)

202
tests/tests/filters.py Normal file
View File

@@ -0,0 +1,202 @@
from __future__ import with_statement
import os
import sys
from unittest2 import skipIf
from django.test import TestCase
from compressor.cache import get_hashed_mtime
from compressor.conf import settings
from compressor.css import CssCompressor
from compressor.utils import find_command
from compressor.filters.base import CompilerFilter
from .templatetags import render
from .base import css_tag, test_dir
class CssTidyTestCase(TestCase):
def test_tidy(self):
content = """
/* Some comment */
font,th,td,p{
color: black;
}
"""
from compressor.filters.csstidy import CSSTidyFilter
self.assertEqual(
"font,th,td,p{color:#000;}", CSSTidyFilter(content).input())
CssTidyTestCase = skipIf(
find_command(settings.COMPRESS_CSSTIDY_BINARY) is None,
'CSStidy binary %r not found' % settings.COMPRESS_CSSTIDY_BINARY
)(CssTidyTestCase)
class CompassTestCase(TestCase):
def setUp(self):
self.old_debug = settings.DEBUG
self.old_compress_css_filters = settings.COMPRESS_CSS_FILTERS
self.old_compress_url = settings.COMPRESS_URL
self.old_enabled = settings.COMPRESS_ENABLED
settings.DEBUG = True
settings.COMPRESS_ENABLED = True
settings.COMPRESS_CSS_FILTERS = [
'compressor.filters.compass.CompassFilter',
'compressor.filters.css_default.CssAbsoluteFilter',
]
settings.COMPRESS_URL = '/media/'
def tearDown(self):
settings.DEBUG = self.old_debug
settings.COMPRESS_URL = self.old_compress_url
settings.COMPRESS_ENABLED = self.old_enabled
settings.COMPRESS_CSS_FILTERS = self.old_compress_css_filters
def test_compass(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/screen.scss" type="text/css">
<link rel="stylesheet" href="{{ MEDIA_URL }}sass/print.scss" type="text/css">
{% endcompress %}
"""
context = {'MEDIA_URL': settings.COMPRESS_URL}
out = css_tag("/media/CACHE/css/8ff1cfd8787d.css")
self.assertEqual(out, render(template, context))
CompassTestCase = skipIf(
find_command(settings.COMPRESS_COMPASS_BINARY) is None,
'Compass binary %r not found' % settings.COMPRESS_COMPASS_BINARY
)(CompassTestCase)
class PrecompilerTestCase(TestCase):
def setUp(self):
self.filename = os.path.join(test_dir, 'media/css/one.css')
with open(self.filename) as f:
self.content = f.read()
self.test_precompiler = os.path.join(test_dir, 'precompiler.py')
def test_precompiler_infile_outfile(self):
command = '%s %s -f {infile} -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_infile_stdout(self):
command = '%s %s -f {infile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())
def test_precompiler_stdin_outfile(self):
command = '%s %s -o {outfile}' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }", compiler.input())
def test_precompiler_stdin_stdout(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=None, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())
def test_precompiler_stdin_stdout_filename(self):
command = '%s %s' % (sys.executable, self.test_precompiler)
compiler = CompilerFilter(content=self.content, filename=self.filename, command=command)
self.assertEqual(u"body { color:#990; }\n", compiler.input())
class CssMinTestCase(TestCase):
def test_cssmin_filter(self):
from compressor.filters.cssmin import CSSMinFilter
content = """p {
background: rgb(51,102,153) url('../../images/image.gif');
}
"""
output = "p{background:#369 url('../../images/image.gif')}"
self.assertEqual(output, CSSMinFilter(content).output())
class CssAbsolutizingTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/url/url1.css" type="text/css">
<link rel="stylesheet" href="/media/css/url/2/url2.css" type="text/css">
"""
self.css_node = CssCompressor(self.css)
def test_css_absolute_filter(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'http://media.example.com/'
filter = CssAbsoluteFilter(content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_https(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_absolute_filter_relative_path(self):
from compressor.filters.css_default import CssAbsoluteFilter
filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
content = "p { background: url('../../images/image.gif') }"
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
filter = CssAbsoluteFilter(content)
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
settings.COMPRESS_URL = 'https://media.example.com/'
filter = CssAbsoluteFilter(content)
output = "p { background: url('%simages/image.gif?%s') }" % (settings.COMPRESS_URL, get_hashed_mtime(filename))
self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
def test_css_hunks(self):
hash_dict = {
'hash1': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/url1.css')),
'hash2': get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/url/2/url2.css')),
}
out = [u"p { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\np { background: url('/media/images/test.png?%(hash1)s'); }\n" % hash_dict,
u"p { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\np { background: url('/media/images/test.png?%(hash2)s'); }\n" % hash_dict]
hunks = [h for m, h in self.css_node.hunks()]
self.assertEqual(out, hunks)
class CssDataUriTestCase(TestCase):
def setUp(self):
settings.COMPRESS_ENABLED = True
settings.COMPRESS_CSS_FILTERS = [
'compressor.filters.css_default.CssAbsoluteFilter',
'compressor.filters.datauri.CssDataUriFilter',
]
settings.COMPRESS_URL = '/media/'
self.css = """
<link rel="stylesheet" href="/media/css/datauri.css" type="text/css">
"""
self.css_node = CssCompressor(self.css)
def test_data_uris(self):
datauri_hash = get_hashed_mtime(os.path.join(settings.COMPRESS_ROOT, 'css/datauri.css'))
out = [u'.add { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAJvSURBVDjLpZPrS5NhGIf9W7YvBYOkhlkoqCklWChv2WyKik7blnNris72bi6dus0DLZ0TDxW1odtopDs4D8MDZuLU0kXq61CijSIIasOvv94VTUfLiB74fXngup7nvrnvJABJ/5PfLnTTdcwOj4RsdYmo5glBWP6iOtzwvIKSWstI0Wgx80SBblpKtE9KQs/We7EaWoT/8wbWP61gMmCH0lMDvokT4j25TiQU/ITFkek9Ow6+7WH2gwsmahCPdwyw75uw9HEO2gUZSkfyI9zBPCJOoJ2SMmg46N61YO/rNoa39Xi41oFuXysMfh36/Fp0b7bAfWAH6RGi0HglWNCbzYgJaFjRv6zGuy+b9It96N3SQvNKiV9HvSaDfFEIxXItnPs23BzJQd6DDEVM0OKsoVwBG/1VMzpXVWhbkUM2K4oJBDYuGmbKIJ0qxsAbHfRLzbjcnUbFBIpx/qH3vQv9b3U03IQ/HfFkERTzfFj8w8jSpR7GBE123uFEYAzaDRIqX/2JAtJbDat/COkd7CNBva2cMvq0MGxp0PRSCPF8BXjWG3FgNHc9XPT71Ojy3sMFdfJRCeKxEsVtKwFHwALZfCUk3tIfNR8XiJwc1LmL4dg141JPKtj3WUdNFJqLGFVPC4OkR4BxajTWsChY64wmCnMxsWPCHcutKBxMVp5mxA1S+aMComToaqTRUQknLTH62kHOVEE+VQnjahscNCy0cMBWsSI0TCQcZc5ALkEYckL5A5noWSBhfm2AecMAjbcRWV0pUTh0HE64TNf0mczcnnQyu/MilaFJCae1nw2fbz1DnVOxyGTlKeZft/Ff8x1BRssfACjTwQAAAABJRU5ErkJggg=="); }\n.python { background-image: url("/media/img/python.png?%s"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n' % datauri_hash]
hunks = [h for m, h in self.css_node.hunks()]
self.assertEqual(out, hunks)

82
tests/tests/offline.py Normal file
View File

@@ -0,0 +1,82 @@
from __future__ import with_statement
import os
from django.template import Template, Context
from django.test import TestCase
from compressor.conf import settings
from compressor.management.commands.compress import Command as CompressCommand
from .base import test_dir, css_tag
class OfflineGenerationTestCase(TestCase):
"""Uses templates/test_compressor_offline.html"""
maxDiff = None
def setUp(self):
self._old_compress = settings.COMPRESS_ENABLED
self._old_compress_offline = settings.COMPRESS_OFFLINE
settings.COMPRESS_ENABLED = True
settings.COMPRESS_OFFLINE = True
self.template_file = open(os.path.join(test_dir, "templates/test_compressor_offline.html"))
self.template = Template(self.template_file.read().decode(settings.FILE_CHARSET))
def tearDown(self):
settings.COMPRESS_ENABLED = self._old_compress
settings.COMPRESS_OFFLINE = self._old_compress_offline
self.template_file.close()
def test_offline(self):
count, result = CompressCommand().compress()
self.assertEqual(5, count)
self.assertEqual([
css_tag('/media/CACHE/css/cd579b7deb7d.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/67ed6aff7f7b.css" type="text/css" />\n',
], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves?
rendered_template = self.template.render(Context({})).replace("\n", "")
self.assertEqual(rendered_template, "".join(result).replace("\n", ""))
def test_offline_with_context(self):
self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT
settings.COMPRESS_OFFLINE_CONTEXT = {
'color': 'blue',
}
count, result = CompressCommand().compress()
self.assertEqual(5, count)
self.assertEqual([
css_tag('/media/CACHE/css/ee62fbfd116a.css')+'\n',
u'<script type="text/javascript" src="/media/CACHE/js/0a2bb9a287c0.js"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/fb1736ad48b7.js"></script>',
u'<script type="text/javascript" src="/media/CACHE/js/770a7311729e.js"></script>',
u'<link rel="stylesheet" href="/media/CACHE/css/73e015f740c6.css" type="text/css" />\n',
], result)
# Template rendering should use the cache. FIXME: how to make sure of it ? Should we test the cache
# key<->values ourselves?
rendered_template = self.template.render(Context(settings.COMPRESS_OFFLINE_CONTEXT)).replace("\n", "")
self.assertEqual(rendered_template, "".join(result).replace("\n", ""))
settings.COMPRESS_OFFLINE_CONTEXT = self._old_offline_context
def test_get_loaders(self):
old_loaders = settings.TEMPLATE_LOADERS
settings.TEMPLATE_LOADERS = (
('django.template.loaders.cached.Loader', (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)),
)
try:
from django.template.loaders.filesystem import Loader as FileSystemLoader
from django.template.loaders.app_directories import Loader as AppDirectoriesLoader
except ImportError:
pass
else:
loaders = CompressCommand().get_loaders()
self.assertTrue(isinstance(loaders[0], FileSystemLoader))
self.assertTrue(isinstance(loaders[1], AppDirectoriesLoader))
finally:
settings.TEMPLATE_LOADERS = old_loaders

80
tests/tests/parsers.py Normal file
View File

@@ -0,0 +1,80 @@
from __future__ import with_statement
import os
from unittest2 import skipIf
from BeautifulSoup import BeautifulSoup
try:
import lxml
except ImportError:
lxml = None
try:
import html5lib
except ImportError:
html5lib = None
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
BeautifulSoup = None
from compressor.conf import settings
from compressor.base import SOURCE_HUNK, SOURCE_FILE
from .base import CompressorTestCase
class ParserTestCase(object):
def setUp(self):
self.old_parser = settings.COMPRESS_PARSER
settings.COMPRESS_PARSER = self.parser_cls
super(ParserTestCase, self).setUp()
def tearDown(self):
settings.COMPRESS_PARSER = self.old_parser
class LxmlParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.LxmlParser'
LxmlParserTests = skipIf(lxml is None, 'lxml not found')(LxmlParserTests)
class Html5LibParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.Html5LibParser'
def test_css_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/one.css'), u'css/one.css', u'<link href="/media/css/one.css" rel="stylesheet" type="text/css">'),
(SOURCE_HUNK, u'p { border:5px solid green;}', None, u'<style type="text/css">p { border:5px solid green;}</style>'),
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css/two.css'), u'css/two.css', u'<link href="/media/css/two.css" rel="stylesheet" type="text/css">'),
]
split = self.css_node.split_contents()
split = [(x[0], x[1], x[2], self.css_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
def test_js_split(self):
out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', u'<script src="/media/js/one.js" type="text/javascript"></script>'),
(SOURCE_HUNK, u'obj.value = "value";', None, u'<script type="text/javascript">obj.value = "value";</script>'),
]
split = self.js_node.split_contents()
split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split]
self.assertEqual(out, split)
Html5LibParserTests = skipIf(
html5lib is None, 'html5lib not found')(Html5LibParserTests)
class BeautifulSoupParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.BeautifulSoupParser'
BeautifulSoupParserTests = skipIf(
BeautifulSoup is None, 'BeautifulSoup not found')(BeautifulSoupParserTests)
class HtmlParserTests(ParserTestCase, CompressorTestCase):
parser_cls = 'compressor.parser.HtmlParser'

33
tests/tests/storages.py Normal file
View File

@@ -0,0 +1,33 @@
from __future__ import with_statement
from django.core.files.storage import get_storage_class
from django.test import TestCase
from compressor import base
from compressor.conf import settings
from .base import css_tag
from .templatetags import render
class StorageTestCase(TestCase):
def setUp(self):
self._storage = base.default_storage
base.default_storage = get_storage_class(
'compressor.storage.GzipCompressorFileStorage')()
settings.COMPRESS_ENABLED = True
def tearDown(self):
base.default_storage = self._storage
def test_css_tag_with_storage(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/one.css" type="text/css">
<style type="text/css">p { border:5px solid white;}</style>
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css">
{% endcompress %}
"""
context = {'MEDIA_URL': settings.COMPRESS_URL}
out = css_tag("/media/CACHE/css/1d4424458f88.css")
self.assertEqual(out, render(template, context))

View File

@@ -0,0 +1,99 @@
from __future__ import with_statement
from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase
from compressor.conf import settings
from .base import css_tag
def render(template_string, context_dict=None):
"""
A shortcut for testing template output.
"""
if context_dict is None:
context_dict = {}
c = Context(context_dict)
t = Template(template_string)
return t.render(c).strip()
class TemplatetagTestCase(TestCase):
def setUp(self):
self.old_enabled = settings.COMPRESS_ENABLED
settings.COMPRESS_ENABLED = True
self.context = {'MEDIA_URL': settings.COMPRESS_URL}
def tearDown(self):
settings.COMPRESS_ENABLED = self.old_enabled
def test_empty_tag(self):
template = u"""{% load compress %}{% compress js %}{% block js %}
{% endblock %}{% endcompress %}"""
self.assertEqual(u'', render(template, self.context))
def test_css_tag(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/one.css" type="text/css">
<style type="text/css">p { border:5px solid green;}</style>
<link rel="stylesheet" href="{{ MEDIA_URL }}css/two.css" type="text/css">
{% endcompress %}"""
out = css_tag("/media/CACHE/css/e41ba2cc6982.css")
self.assertEqual(out, render(template, self.context))
def test_nonascii_css_tag(self):
template = u"""{% load compress %}{% compress css %}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/nonasc.css" type="text/css">
<style type="text/css">p { border:5px solid green;}</style>
{% endcompress %}
"""
out = css_tag("/media/CACHE/css/799f6defe43c.css")
self.assertEqual(out, render(template, self.context))
def test_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/one.js" type="text/javascript"></script>
<script type="text/javascript">obj.value = "value";</script>
{% endcompress %}
"""
out = u'<script type="text/javascript" src="/media/CACHE/js/066cd253eada.js"></script>'
self.assertEqual(out, render(template, self.context))
def test_nonascii_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/nonasc.js" type="text/javascript"></script>
<script type="text/javascript">var test_value = "\u2014";</script>
{% endcompress %}
"""
out = u'<script type="text/javascript" src="/media/CACHE/js/e214fe629b28.js"></script>'
self.assertEqual(out, render(template, self.context))
def test_nonascii_latin1_js_tag(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/nonasc-latin1.js" type="text/javascript" charset="latin-1"></script>
<script type="text/javascript">var test_value = "\u2014";</script>
{% endcompress %}
"""
out = u'<script type="text/javascript" src="/media/CACHE/js/be9e078b5ca7.js"></script>'
self.assertEqual(out, render(template, self.context))
def test_compress_tag_with_illegal_arguments(self):
template = u"""{% load compress %}{% compress pony %}
<script type="pony/application">unicorn</script>
{% endcompress %}"""
self.assertRaises(TemplateSyntaxError, render, template, {})
def test_debug_toggle(self):
template = u"""{% load compress %}{% compress js %}
<script src="{{ MEDIA_URL }}js/one.js" type="text/javascript"></script>
<script type="text/javascript">obj.value = "value";</script>
{% endcompress %}
"""
class MockDebugRequest(object):
GET = {settings.COMPRESS_DEBUG_TOGGLE: 'true'}
context = dict(self.context, request=MockDebugRequest())
out = u"""<script src="/media/js/one.js" type="text/javascript"></script>
<script type="text/javascript">obj.value = "value";</script>"""
self.assertEqual(out, render(template, context))

View File

@@ -1,12 +1,15 @@
[testenv]
[tox]
setupdir = ..
distribute = false
sitepackages = true
downloadcache = {toxinidir}/_download/
[testenv]
commands =
{envpython} compressor/tests/runtests.py []
{envpython} {toxinidir}/runtests.py []
coverage html -d {envtmpdir}/coverage
[testenv:docs]
changedir = docs
changedir = ../docs
deps =
Sphinx
commands =