Commit a146db83 authored by Thomas Goirand's avatar Thomas Goirand

Merge tag '2.2' into debian/pike

parents e89440ab 8abd5869
......@@ -2,6 +2,8 @@ build
compressor/tests/static/CACHE
compressor/tests/static/custom
compressor/tests/static/js/066cd253eada.js
compressor/tests/static/js/d728fc7f9301.js
compressor/tests/static/js/74e158ccb432.js
compressor/tests/static/test.txt*
dist
......
language: python
sudo: false
install:
- if [[ $TOXENV == py32-1.8.X ]]; then pip install pip\<8.0.0 virtualenv\<14.0.0; fi
- pip install tox
script:
- tox
env:
- TOXENV=py27-1.8.X
- TOXENV=py32-1.8.X
- TOXENV=py33-1.8.X
- TOXENV=py34-1.8.X
- TOXENV=py27-1.9.X
- TOXENV=py34-1.9.X
- TOXENV=py27-1.10.X
- TOXENV=py34-1.10.X
- TOXENV=py27-1.11.X
- TOXENV=py34-1.11.X
# https://github.com/travis-ci/travis-ci/issues/4794
matrix:
include:
......@@ -23,6 +23,10 @@ matrix:
env: TOXENV=py35-1.9.X
- python: 3.5
env: TOXENV=py35-1.10.X
- python: 3.5
env: TOXENV=py35-1.11.X
- python: 3.6
env: TOXENV=py36-1.11.X
notifications:
irc: "irc.freenode.org#django-compressor"
after_success:
......
# following PEP 386
__version__ = "2.1.1"
__version__ = "2.2"
......@@ -3,21 +3,22 @@ import os
import codecs
from importlib import import_module
from django import VERSION
from django.core.files.base import ContentFile
from django.utils.safestring import mark_safe
from django.utils.six.moves.urllib.request import url2pathname
from django.template.loader import render_to_string
from django.utils.functional import cached_property
from compressor.cache import get_hexdigest, get_mtime
from compressor.conf import settings
from compressor.exceptions import (CompressorError, UncompressableFileError,
FilterDoesNotExist)
from compressor.filters import CachedCompilerFilter
from compressor.filters.css_default import CssAbsoluteFilter
from compressor.filters.css_default import CssAbsoluteFilter, CssRelativeFilter
from compressor.storage import compressor_file_storage
from compressor.signals import post_compress
from compressor.utils import get_class, get_mod_func, staticfiles
from compressor.utils.decorators import cached_property
# Some constants for nicer handling.
SOURCE_HUNK, SOURCE_FILE = 'inline', 'file'
......@@ -91,12 +92,12 @@ class Compressor(object):
Returns file path for an output file based on contents.
Returned path is relative to compressor storage's base url, for
example "CACHE/css/e41ba2cc6982.css".
example "CACHE/css/58a8c0714e59.css".
When `basename` argument is provided then file name (without extension)
will be used as a part of returned file name, for example:
get_filepath(content, "my_file.css") -> 'CACHE/css/my_file.e41ba2cc6982.css'
get_filepath(content, "my_file.css") -> 'CACHE/css/my_file.58a8c0714e59.css'
"""
parts = []
if basename:
......@@ -211,6 +212,8 @@ class Compressor(object):
# on precompiled css files even if compression is disabled.
if CssAbsoluteFilter in self.cached_filters:
value = self.filter(value, [CssAbsoluteFilter], **options)
elif CssRelativeFilter in self.cached_filters:
value = self.filter(value, [CssRelativeFilter], **options)
yield self.handle_output(kind, value, forced=True,
basename=basename)
else:
......@@ -339,9 +342,11 @@ class Compressor(object):
self.context['compressed'].update(context or {})
self.context['compressed'].update(self.extra_context)
if hasattr(self.context, 'flatten'):
# Django 1.8 complains about Context being passed to its
# Template.render function.
if hasattr(self.context, 'flatten') and VERSION >= (1, 9):
# Passing Contexts to Template.render is deprecated since Django 1.8.
# However, we use the fix below only for django 1.9 and above, since
# the flatten method is buggy in 1.8, see https://code.djangoproject.com/ticket/24765
final_context = self.context.flatten()
else:
final_context = self.context
......
......@@ -18,7 +18,7 @@ _cachekey_func = None
def get_hexdigest(plaintext, length=None):
digest = hashlib.md5(smart_bytes(plaintext)).hexdigest()
digest = hashlib.sha256(smart_bytes(plaintext)).hexdigest()
if length:
return digest[:length]
return digest
......@@ -50,7 +50,10 @@ def get_mtime_cachekey(filename):
def get_offline_hexdigest(render_template_string):
return get_hexdigest(render_template_string)
return get_hexdigest(
# Make the hexdigest determination independent of STATIC_URL
render_template_string.replace(settings.STATIC_URL, '')
)
def get_offline_cachekey(source):
......
......@@ -70,6 +70,9 @@ class CompressorConf(AppConf):
OFFLINE_MANIFEST = 'manifest.json'
# The Context to be used when TemplateFilter is used
TEMPLATE_FILTER_CONTEXT = {}
# Placeholder to be used instead of settings.COMPRESS_URL during offline compression.
# Affects manifest file contents only.
URL_PLACEHOLDER = '/__compressor_url_placeholder__/'
# Returns the Jinja2 environment to use in offline compression.
def JINJA2_GET_ENVIRONMENT():
......
......@@ -16,7 +16,7 @@ class CssCompressor(Compressor):
data = None
elem_name = self.parser.elem_name(elem)
elem_attribs = self.parser.elem_attribs(elem)
if elem_name == 'link' and elem_attribs['rel'].lower() == 'stylesheet':
if elem_name == 'link' and 'rel' in elem_attribs and elem_attribs['rel'].lower() == 'stylesheet':
basename = self.get_basename(elem_attribs['href'])
filename = self.get_filename(basename)
data = (SOURCE_FILE, filename, basename, elem)
......
......@@ -6,9 +6,16 @@ from compressor.cache import get_hashed_mtime, get_hashed_content
from compressor.conf import settings
from compressor.filters import FilterBase, FilterError
URL_PATTERN = re.compile(r'url\(([^\)]+)\)')
SRC_PATTERN = re.compile(r'src=([\'"])(.+?)\1')
SCHEMES = ('http://', 'https://', '/', 'data:')
URL_PATTERN = re.compile(r"""
url\(
\s* # any amount of whitespace
([\'"]?) # optional quote
(.*?) # any amount of anything, non-greedily (this is the actual url)
\1 # matching quote (or nothing if there was none)
\s* # any amount of whitespace
\)""", re.VERBOSE)
SRC_PATTERN = re.compile(r'src=([\'"])(.*?)\1')
SCHEMES = ('http://', 'https://', '/')
class CssAbsoluteFilter(FilterBase):
......@@ -56,51 +63,92 @@ class CssAbsoluteFilter(FilterBase):
def add_suffix(self, url):
filename = self.guess_filename(url)
suffix = None
if filename:
if settings.COMPRESS_CSS_HASHING_METHOD == "mtime":
suffix = get_hashed_mtime(filename)
elif settings.COMPRESS_CSS_HASHING_METHOD in ("hash", "content"):
suffix = get_hashed_content(filename)
elif settings.COMPRESS_CSS_HASHING_METHOD is None:
suffix = None
else:
raise FilterError('COMPRESS_CSS_HASHING_METHOD is configured '
'with an unknown method (%s).' %
settings.COMPRESS_CSS_HASHING_METHOD)
if suffix is None:
if not filename:
return url
if settings.COMPRESS_CSS_HASHING_METHOD is None:
return url
if not url.startswith(SCHEMES):
return url
if url.startswith(SCHEMES):
fragment = None
if "#" in url:
url, fragment = url.rsplit("#", 1)
if "?" in url:
url = "%s&%s" % (url, suffix)
else:
url = "%s?%s" % (url, suffix)
if fragment is not None:
url = "%s#%s" % (url, fragment)
return url
def _converter(self, matchobj, group, template):
url = matchobj.group(group)
url = url.strip()
wrap = '"' if url[0] == '"' else "'"
url = url.strip('\'"')
suffix = None
if settings.COMPRESS_CSS_HASHING_METHOD == "mtime":
suffix = get_hashed_mtime(filename)
elif settings.COMPRESS_CSS_HASHING_METHOD in ("hash", "content"):
suffix = get_hashed_content(filename)
else:
raise FilterError('COMPRESS_CSS_HASHING_METHOD is configured '
'with an unknown method (%s).' %
settings.COMPRESS_CSS_HASHING_METHOD)
fragment = None
if "#" in url:
url, fragment = url.rsplit("#", 1)
if "?" in url:
url = "%s&%s" % (url, suffix)
else:
url = "%s?%s" % (url, suffix)
if fragment is not None:
url = "%s#%s" % (url, fragment)
return url
if url.startswith('#'):
return template % (wrap, url, wrap)
def _converter(self, url):
if url.startswith(('#', 'data:')):
return url
elif url.startswith(SCHEMES):
return template % (wrap, self.add_suffix(url), wrap)
return self.add_suffix(url)
full_url = posixpath.normpath('/'.join([str(self.directory_name),
url]))
if self.has_scheme:
full_url = "%s%s" % (self.protocol, full_url)
return template % (wrap, self.add_suffix(full_url), wrap)
full_url = self.add_suffix(full_url)
return self.post_process_url(full_url)
def post_process_url(self, url):
"""
Extra URL processing, to be overridden in subclasses.
"""
return url
def url_converter(self, matchobj):
return self._converter(matchobj, 1, "url(%s%s%s)")
quote = matchobj.group(1)
converted_url = self._converter(matchobj.group(2))
return "url(%s%s%s)" % (quote, converted_url, quote)
def src_converter(self, matchobj):
return self._converter(matchobj, 2, "src=%s%s%s")
quote = matchobj.group(1)
converted_url = self._converter(matchobj.group(2))
return "src=%s%s%s" % (quote, converted_url, quote)
class CssRelativeFilter(CssAbsoluteFilter):
"""
Do similar to ``CssAbsoluteFilter`` URL processing
but add a *relative URL prefix* instead of ``settings.COMPRESS_URL``.
"""
def post_process_url(self, url):
"""
Replace ``settings.COMPRESS_URL`` URL prefix with '../' * (N + 1)
where N is the *depth* of ``settings.COMPRESS_OUTPUT_DIR`` folder.
E.g. by default ``settings.COMPRESS_OUTPUT_DIR == 'CACHE'``,
the depth is 1, and the prefix will be '../../'.
If ``settings.COMPRESS_OUTPUT_DIR == 'my/compiled/data'``,
the depth is 3, and the prefix will be '../../../../'.
Example:
- original file URL: '/static/my-app/style.css'
- it has an image link: ``url(images/logo.svg)``
- compiled file URL: '/static/CACHE/css/abcdef123456.css'
- replaced image link URL: ``url(../../my-app/images/logo.svg)``
"""
old_prefix = self.url
if self.has_scheme:
old_prefix = '{}{}'.format(self.protocol, old_prefix)
# One level up from 'css' / 'js' folder
new_prefix = '..'
# N levels up from ``settings.COMPRESS_OUTPUT_DIR``
new_prefix += '/..' * len(list(filter(
None, os.path.normpath(settings.COMPRESS_OUTPUT_DIR).split(os.sep)
)))
return re.sub('^{}'.format(old_prefix), new_prefix, url)
......@@ -50,3 +50,20 @@ class JsCompressor(Compressor):
ret.append(subnode.output(*args, **kwargs))
return '\n'.join(ret)
return super(JsCompressor, self).output(*args, **kwargs)
def filter_input(self, forced=False):
"""
Passes each hunk (file or code) to the 'input' methods
of the compressor filters.
"""
content = []
for hunk in self.hunks(forced):
# If a file ends with a function call, say, console.log()
# but doesn't have a semicolon, and the next file starts with
# a (, the individual files are ok, but when combined you get an
# error like TypeError...
# Forcing a semicolon in between fixes it.
if settings.COMPRESS_ENABLED or forced:
hunk = ";" + hunk
content.append(hunk)
return content
from __future__ import unicode_literals
# flake8: noqa
import os
import sys
......@@ -11,10 +12,11 @@ from django.core.management.base import BaseCommand, CommandError
import django.template
from django.template import Context
from django.utils import six
from django.utils.encoding import smart_text
from django.template.loader import get_template # noqa Leave this in to preload template locations
from django.template import engines
from compressor.cache import get_offline_hexdigest, write_offline_manifest
from compressor.cache import get_offline_hexdigest, write_offline_manifest, get_offline_manifest
from compressor.conf import settings
from compressor.exceptions import (OfflineGenerationError, TemplateSyntaxError,
TemplateDoesNotExist)
......@@ -47,9 +49,11 @@ class Command(BaseCommand):
"(which defaults to STATIC_ROOT). Be aware that using this "
"can lead to infinite recursion if a link points to a parent "
"directory of itself.", dest='follow_links')
parser.add_argument('--engine', default="django", action="store",
help="Specifies the templating engine. jinja2 or django",
dest="engine")
parser.add_argument('--engine', default=[], action="append",
help="Specifies the templating engine. jinja2 and django are "
"supported. It may be a specified more than once for "
"multiple engines. If not specified, django engine is used.",
dest="engines")
def get_loaders(self):
template_source_loaders = []
......@@ -119,7 +123,7 @@ class Command(BaseCommand):
'get_template_sources', None)
if get_template_sources is None:
get_template_sources = loader.get_template_sources
paths.update(str(origin) for origin in get_template_sources(''))
paths.update(smart_text(origin) for origin in get_template_sources(''))
except (ImportError, AttributeError, TypeError):
# Yeah, this didn't work out so well, let's move on
pass
......@@ -140,7 +144,7 @@ class Command(BaseCommand):
templates.update(os.path.join(root, name)
for name in files if not name.startswith('.') and
any(fnmatch(name, "*%s" % glob) for glob in extensions))
elif engine == 'jinja2' and django.VERSION >= (1, 8):
elif engine == 'jinja2':
env = settings.COMPRESS_JINJA2_GET_ENVIRONMENT()
if env and hasattr(env, 'list_templates'):
templates |= set([env.loader.get_source(env, template)[1] for template in
......@@ -177,7 +181,7 @@ class Command(BaseCommand):
continue
except TemplateSyntaxError as e: # broken template -> ignore
if verbosity > 0:
log.write("Invalid template %s: %s\n" % (template_name, e))
log.write("Invalid template %s: %s\n" % (template_name, smart_text(e)))
continue
except TemplateDoesNotExist: # non existent template -> ignore
if verbosity > 0:
......@@ -197,7 +201,8 @@ class Command(BaseCommand):
except (TemplateDoesNotExist, TemplateSyntaxError) as e:
# Could be an error in some base template
if verbosity > 0:
log.write("Error parsing template %s: %s\n" % (template_name, e))
log.write("Error parsing template %s: %s\n" %
(template_name, smart_text(e)))
continue
if nodes:
template.template_name = template_name
......@@ -244,7 +249,10 @@ class Command(BaseCommand):
result = parser.render_node(template, context, node)
except Exception as e:
raise CommandError("An error occurred during rendering %s: "
"%s" % (template.template_name, e))
"%s" % (template.template_name, smart_text(e)))
result = result.replace(
settings.COMPRESS_URL, settings.COMPRESS_URL_PLACEHOLDER
)
offline_manifest[key] = result
context.pop()
results.append(result)
......@@ -288,8 +296,16 @@ class Command(BaseCommand):
raise CommandError(
"Offline compression is disabled. Set "
"COMPRESS_OFFLINE or use the --force to override.")
self.compress(sys.stdout, **options)
options.setdefault("log", sys.stdout)
manifest = {}
engines = [e.strip() for e in options.get("engines", [])] or ["django"]
for engine in engines:
opts = options.copy()
opts["engine"] = engine
self.compress(**opts)
manifest.update(get_offline_manifest())
write_offline_manifest(manifest)
Command.requires_system_checks = False
......@@ -116,7 +116,7 @@ class DjangoParser(object):
def render_node(self, template, context, node):
return node.render(context, forced=True)
def get_nodelist(self, node, original, context):
def get_nodelist(self, node, original, context=None):
if isinstance(node, ExtendsNode):
try:
if context is None:
......
......@@ -7,20 +7,10 @@ from compressor.exceptions import ParserError
from compressor.parser import ParserBase
# Starting in Python 3.2, the HTMLParser constructor takes a 'strict'
# argument which default to True (which we don't want).
# In Python 3.3, it defaults to False.
# In Python 3.4, passing it at all raises a deprecation warning.
# So we only pass it for 3.2.
# In Python 3.4, it also takes a 'convert_charrefs' argument
# which raises a warning if we don't pass it.
major, minor, release = sys.version_info[:3]
CONSTRUCTOR_TAKES_STRICT = major == 3 and minor == 2
CONSTRUCTOR_TAKES_CONVERT_CHARREFS = major == 3 and minor >= 4
# Since Python 3.4, the HTMLParser constructor takes a 'convert_charrefs'
# argument which raises a warning if we don't pass it.
HTML_PARSER_ARGS = {}
if CONSTRUCTOR_TAKES_STRICT:
HTML_PARSER_ARGS['strict'] = False
if CONSTRUCTOR_TAKES_CONVERT_CHARREFS:
if sys.version_info[:2] >= (3, 4):
HTML_PARSER_ARGS['convert_charrefs'] = False
......
from __future__ import absolute_import
from django.core.exceptions import ImproperlyConfigured
from django.utils.encoding import smart_text
from django.utils.functional import cached_property
from compressor.exceptions import ParserError
from compressor.parser import ParserBase
from compressor.utils.decorators import cached_property
class Html5LibParser(ParserBase):
......
......@@ -3,10 +3,10 @@ from __future__ import absolute_import, unicode_literals
from django.core.exceptions import ImproperlyConfigured
from django.utils import six
from django.utils.encoding import smart_text
from django.utils.functional import cached_property
from compressor.exceptions import ParserError
from compressor.parser import ParserBase
from compressor.utils.decorators import cached_property
class LxmlParser(ParserBase):
......
......@@ -61,14 +61,18 @@ class CompressorMixin(object):
If enabled and in offline mode, and not forced check the offline cache
and return the result if given
"""
key = get_offline_hexdigest(self.get_original_content(context))
original_content = self.get_original_content(context)
key = get_offline_hexdigest(original_content)
offline_manifest = get_offline_manifest()
if key in offline_manifest:
return offline_manifest[key]
return offline_manifest[key].replace(
settings.COMPRESS_URL_PLACEHOLDER, settings.COMPRESS_URL
)
else:
raise OfflineGenerationError('You have offline compression '
'enabled but key "%s" is missing from offline manifest. '
'You may need to run "python manage.py compress".' % key)
'You may need to run "python manage.py compress". Here '
'is the original content:\n\n%s' % (key, original_content))
def render_cached(self, compressor, kind, mode):
"""
......
......@@ -57,10 +57,10 @@ class PrecompilerAndAbsoluteFilterTestCase(SimpleTestCase):
def setUp(self):
self.html_orig = '<link rel="stylesheet" href="/static/css/relative_url.css" type="text/css" />'
self.html_link_to_precompiled_css = '<link rel="stylesheet" href="/static/CACHE/css/relative_url.41a74f6d5864.css" type="text/css" />'
self.html_link_to_absolutized_css = '<link rel="stylesheet" href="/static/CACHE/css/relative_url.9b8fd415e521.css" type="text/css" />'
self.html_link_to_precompiled_css = '<link rel="stylesheet" href="/static/CACHE/css/relative_url.e8602322bfa6.css" type="text/css" />'
self.html_link_to_absolutized_css = '<link rel="stylesheet" href="/static/CACHE/css/relative_url.376db5682982.css" type="text/css" />'
self.css_orig = "p { background: url('../img/python.png'); }" # content of relative_url.css
self.css_absolutized = "p { background: url('/static/img/python.png?c2281c83670e'); }"
self.css_absolutized = "p { background: url('/static/img/python.png?ccb38978f900'); }"
def helper(self, enabled, use_precompiler, use_absolute_filter, expected_output):
precompiler = (('text/css', 'compressor.tests.test_base.PassthroughPrecompiler'),) if use_precompiler else ()
......@@ -178,10 +178,10 @@ class CompressorTestCase(SimpleTestCase):
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}'")
r"cachekey is returning something that doesn't look like r'\w{12}'")
def test_css_return_if_on(self):
output = css_tag('/static/CACHE/css/e41ba2cc6982.css')
output = css_tag('/static/CACHE/css/58a8c0714e59.css')
self.assertEqual(output, self.css_node.output().strip())
def test_js_split(self):
......@@ -208,17 +208,17 @@ class CompressorTestCase(SimpleTestCase):
self.assertEqual(out, list(self.js_node.hunks()))
def test_js_output(self):
out = '<script type="text/javascript" src="/static/CACHE/js/066cd253eada.js"></script>'
out = '<script type="text/javascript" src="/static/CACHE/js/74e158ccb432.js"></script>'
self.assertEqual(out, self.js_node.output())
def test_js_override_url(self):
self.js_node.context.update({'url': 'This is not a url, just a text'})
out = '<script type="text/javascript" src="/static/CACHE/js/066cd253eada.js"></script>'
out = '<script type="text/javascript" src="/static/CACHE/js/74e158ccb432.js"></script>'
self.assertEqual(out, self.js_node.output())
def test_css_override_url(self):
self.css_node.context.update({'url': 'This is not a url, just a text'})
output = css_tag('/static/CACHE/css/e41ba2cc6982.css')
output = css_tag('/static/CACHE/css/58a8c0714e59.css')
self.assertEqual(output, self.css_node.output().strip())
@override_settings(COMPRESS_PRECOMPILERS=(), COMPRESS_ENABLED=False)
......@@ -226,22 +226,22 @@ class CompressorTestCase(SimpleTestCase):
self.assertEqualCollapsed(self.js, self.js_node.output())
def test_js_return_if_on(self):
output = '<script type="text/javascript" src="/static/CACHE/js/066cd253eada.js"></script>'
output = '<script type="text/javascript" src="/static/CACHE/js/74e158ccb432.js"></script>'
self.assertEqual(output, self.js_node.output())
@override_settings(COMPRESS_OUTPUT_DIR='custom')
def test_custom_output_dir1(self):
output = '<script type="text/javascript" src="/static/custom/js/066cd253eada.js"></script>'
output = '<script type="text/javascript" src="/static/custom/js/74e158ccb432.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
@override_settings(COMPRESS_OUTPUT_DIR='')
def test_custom_output_dir2(self):
output = '<script type="text/javascript" src="/static/js/066cd253eada.js"></script>'
output = '<script type="text/javascript" src="/static/js/74e158ccb432.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
@override_settings(COMPRESS_OUTPUT_DIR='/custom/nested/')
def test_custom_output_dir3(self):
output = '<script type="text/javascript" src="/static/custom/nested/js/066cd253eada.js"></script>'
output = '<script type="text/javascript" src="/static/custom/nested/js/74e158ccb432.js"></script>'
self.assertEqual(output, JsCompressor(self.js).output())
@override_settings(COMPRESS_PRECOMPILERS=(
......@@ -353,6 +353,21 @@ class JsAsyncDeferTestCase(SimpleTestCase):
self.assertEqual(output, attrs)
class JSWithParensTestCase(SimpleTestCase):
def setUp(self):
self.js = """
<script src="/static/js/one.js"></script>
<script src="/static/js/two.js"></script>
"""
def test_js_content(self):
js_node = JsCompressor(self.js)
content = js_node.filter_input()
self.assertEqual(content[0], ';obj = {};')
self.assertEqual(content[1], ';pollos = {}')
class CacheTestCase(SimpleTestCase):
def setUp(self):
......
This diff is collapsed.
# -*- coding: utf-8 -*-
from __future__ import with_statement, unicode_literals
import sys
import unittest
from django.test import TestCase
from django.utils import six
from django.test.utils import override_settings
from compressor.conf import settings
from compressor.tests.test_base import css_tag
@unittest.skipIf(six.PY3 and sys.version_info[:2] < (3, 3),
'Jinja can only run on Python < 3 and >= 3.3')
class TestJinja2CompressorExtension(TestCase):
"""
Test case for jinja2 extension.
......@@ -83,7 +77,7 @@ class TestJinja2CompressorExtension(TestCase):
<link rel="stylesheet" href="{{ STATIC_URL }}css/two.css" type="text/css" charset="utf-8">
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = css_tag("/static/CACHE/css/e41ba2cc6982.css")
out = css_tag("/static/CACHE/css/58a8c0714e59.css")
self.assertEqual(out, template.render(context))
def test_nonascii_css_tag(self):
......@@ -92,7 +86,7 @@ class TestJinja2CompressorExtension(TestCase):
<style type="text/css">p { border:5px solid green;}</style>
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = css_tag("/static/CACHE/css/799f6defe43c.css")
out = css_tag("/static/CACHE/css/4263023f49d6.css")
self.assertEqual(out, template.render(context))
def test_js_tag(self):
......@@ -101,7 +95,7 @@ class TestJinja2CompressorExtension(TestCase):
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = '<script type="text/javascript" src="/static/CACHE/js/066cd253eada.js"></script>'
out = '<script type="text/javascript" src="/static/CACHE/js/74e158ccb432.js"></script>'
self.assertEqual(out, template.render(context))
def test_nonascii_js_tag(self):
......@@ -110,7 +104,7 @@ class TestJinja2CompressorExtension(TestCase):
<script type="text/javascript" charset="utf-8">var test_value = "\u2014";</script>
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = '<script type="text/javascript" src="/static/CACHE/js/e214fe629b28.js"></script>'
out = '<script type="text/javascript" src="/static/CACHE/js/a18195c6ae48.js"></script>'
self.assertEqual(out, template.render(context))
def test_nonascii_latin1_js_tag(self):
......@@ -119,7 +113,7 @@ class TestJinja2CompressorExtension(TestCase):
<script type="text/javascript">var test_value = "\u2014";</script>
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = '<script type="text/javascript" src="/static/CACHE/js/be9e078b5ca7.js"></script>'
out = '<script type="text/javascript" src="/static/CACHE/js/f64debbd8878.js"></script>'
self.assertEqual(out, template.render(context))
def test_css_inline(self):
......@@ -140,7 +134,7 @@ class TestJinja2CompressorExtension(TestCase):
<script type="text/javascript" charset="utf-8">obj.value = "value";</script>
{% endcompress %}""")
context = {'STATIC_URL': settings.COMPRESS_URL}
out = '<script type="text/javascript">obj={};obj.value="value";</script>'
out = '<script type="text/javascript">;obj={};;obj.value="value";</script>'
self.assertEqual(out, template.render(context))
def test_nonascii_inline_css(self):
......@@ -149,6 +143,6 @@ class TestJinja2CompressorExtension(TestCase):
'<style type="text/css">'
'/* русский текст */'
'</style>{% endcompress %}')
out = '<link rel="stylesheet" href="/static/CACHE/css/b2cec0f8cb24.css" type="text/css" />'
out = '<link rel="stylesheet" href="/static/CACHE/css/c836c9caed5c.css" type="text/css" />'
context = {'STATIC_URL': settings.COMPRESS_URL}
self.assertEqual(out, template.render(context))
......@@ -8,7 +8,7 @@ class TestMtimeCacheCommand(TestCase):
# FIXME: add actual tests, improve the existing ones.
exclusion_patterns = [
'*CACHE*', '*custom*', '*066cd253eada.js', 'test.txt*'
'*CACHE*', '*custom*', '*066cd253eada.js', '*d728fc7f9301.js', '*74e158ccb432.js', 'test.txt*'
]
def default_ignore(self):
......@@ -24,8 +24,8 @@ class TestMtimeCacheCommand(TestCase):
call_command(
'mtime_cache', '--add', *self.default_ignore(), stdout=out)
output = out.getvalue()
self.assertIn('Deleted mtimes of 19 files from the cache.', output)
self.assertIn('Added mtimes of 19 files to cache.', output)
self.assertIn('Deleted mtimes of 20 files from the cache.', output)
self.assertIn('Added mtimes of 20 files to cache.', output)
def test_handle_clean(self):
out = StringIO()
......@@ -33,5 +33,5 @@ class TestMtimeCacheCommand(TestCase):
call_command(
'mtime_cache', '--clean', *self.default_ignore(), stdout=out)
output = out.getvalue()
self.assertIn('Deleted mtimes of 19 files from the cache.', output)
self.assertNotIn('Added mtimes of 19 files to cache.', output)
self.assertIn('Deleted mtimes of 20 files from the cache.', output)
self.assertNotIn('Added mtimes of 20 files to cache.', output)
This diff is collapsed.