diff --git a/MANIFEST.in b/MANIFEST.in
index 6603e89..7b51196 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -2,4 +2,4 @@ include AUTHORS
include README.rst
include LICENSE
recursive-include compressor/templates/compressor *.html
-recursive-include tests *.js *.css *.png *.py
+recursive-include compressor/tests *.js *.css *.png
\ No newline at end of file
diff --git a/compressor/tests/__init__.py b/compressor/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/compressor/tests/media/css/datauri.css b/compressor/tests/media/css/datauri.css
new file mode 100644
index 0000000..6d45b40
--- /dev/null
+++ b/compressor/tests/media/css/datauri.css
@@ -0,0 +1,3 @@
+.add { background-image: url("../img/add.png"); }
+.python { background-image: url("../img/python.png"); }
+.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }
diff --git a/compressor/tests/media/css/nonasc.css b/compressor/tests/media/css/nonasc.css
new file mode 100644
index 0000000..43159ab
--- /dev/null
+++ b/compressor/tests/media/css/nonasc.css
@@ -0,0 +1 @@
+.byline:before { content: " — "; }
\ No newline at end of file
diff --git a/compressor/tests/media/css/one.css b/compressor/tests/media/css/one.css
new file mode 100644
index 0000000..769b83f
--- /dev/null
+++ b/compressor/tests/media/css/one.css
@@ -0,0 +1 @@
+body { background:#990; }
\ No newline at end of file
diff --git a/compressor/tests/media/css/two.css b/compressor/tests/media/css/two.css
new file mode 100644
index 0000000..b73f594
--- /dev/null
+++ b/compressor/tests/media/css/two.css
@@ -0,0 +1 @@
+body { color:#fff; }
\ No newline at end of file
diff --git a/compressor/tests/media/css/url/2/url2.css b/compressor/tests/media/css/url/2/url2.css
new file mode 100644
index 0000000..48e20a5
--- /dev/null
+++ b/compressor/tests/media/css/url/2/url2.css
@@ -0,0 +1,4 @@
+p { background: url('../../../img/add.png'); }
+p { background: url(../../../img/add.png); }
+p { background: url( ../../../img/add.png ); }
+p { background: url( '../../../img/add.png' ); }
diff --git a/compressor/tests/media/css/url/nonasc.css b/compressor/tests/media/css/url/nonasc.css
new file mode 100644
index 0000000..2afa456
--- /dev/null
+++ b/compressor/tests/media/css/url/nonasc.css
@@ -0,0 +1,2 @@
+p { background: url( '../../images/test.png' ); }
+.byline:before { content: " — "; }
\ No newline at end of file
diff --git a/compressor/tests/media/css/url/test.css b/compressor/tests/media/css/url/test.css
new file mode 100644
index 0000000..0f6edf8
--- /dev/null
+++ b/compressor/tests/media/css/url/test.css
@@ -0,0 +1 @@
+p { background: url('/media/images/image.gif') }
\ No newline at end of file
diff --git a/compressor/tests/media/css/url/url1.css b/compressor/tests/media/css/url/url1.css
new file mode 100644
index 0000000..e77e922
--- /dev/null
+++ b/compressor/tests/media/css/url/url1.css
@@ -0,0 +1,4 @@
+p { background: url('../../img/python.png'); }
+p { background: url(../../img/python.png); }
+p { background: url( ../../img/python.png ); }
+p { background: url( '../../img/python.png' ); }
diff --git a/compressor/tests/media/custom/js/066cd253eada.js b/compressor/tests/media/custom/js/066cd253eada.js
new file mode 100755
index 0000000..7a0f97f
--- /dev/null
+++ b/compressor/tests/media/custom/js/066cd253eada.js
@@ -0,0 +1 @@
+obj={};obj.value="value";
\ No newline at end of file
diff --git a/compressor/tests/media/custom/nested/js/066cd253eada.js b/compressor/tests/media/custom/nested/js/066cd253eada.js
new file mode 100755
index 0000000..7a0f97f
--- /dev/null
+++ b/compressor/tests/media/custom/nested/js/066cd253eada.js
@@ -0,0 +1 @@
+obj={};obj.value="value";
\ No newline at end of file
diff --git a/compressor/tests/media/img/add.png b/compressor/tests/media/img/add.png
new file mode 100644
index 0000000..6332fef
Binary files /dev/null and b/compressor/tests/media/img/add.png differ
diff --git a/compressor/tests/media/img/python.png b/compressor/tests/media/img/python.png
new file mode 100644
index 0000000..738f6ed
Binary files /dev/null and b/compressor/tests/media/img/python.png differ
diff --git a/compressor/tests/media/js/066cd253eada.js b/compressor/tests/media/js/066cd253eada.js
new file mode 100755
index 0000000..7a0f97f
--- /dev/null
+++ b/compressor/tests/media/js/066cd253eada.js
@@ -0,0 +1 @@
+obj={};obj.value="value";
\ No newline at end of file
diff --git a/compressor/tests/media/js/nonasc-latin1.js b/compressor/tests/media/js/nonasc-latin1.js
new file mode 100644
index 0000000..109aa20
--- /dev/null
+++ b/compressor/tests/media/js/nonasc-latin1.js
@@ -0,0 +1 @@
+var test_value = "Überstríng";
diff --git a/compressor/tests/media/js/nonasc.js b/compressor/tests/media/js/nonasc.js
new file mode 100644
index 0000000..838a628
--- /dev/null
+++ b/compressor/tests/media/js/nonasc.js
@@ -0,0 +1 @@
+var test_value = "—";
diff --git a/compressor/tests/media/js/one.coffee b/compressor/tests/media/js/one.coffee
new file mode 100644
index 0000000..57bf896
--- /dev/null
+++ b/compressor/tests/media/js/one.coffee
@@ -0,0 +1 @@
+# this is a comment.
diff --git a/compressor/tests/media/js/one.js b/compressor/tests/media/js/one.js
new file mode 100644
index 0000000..b7d2a00
--- /dev/null
+++ b/compressor/tests/media/js/one.js
@@ -0,0 +1 @@
+obj = {};
\ No newline at end of file
diff --git a/compressor/tests/models.py b/compressor/tests/models.py
new file mode 100644
index 0000000..e69de29
diff --git a/compressor/tests/precompiler.py b/compressor/tests/precompiler.py
new file mode 100644
index 0000000..e62a4b4
--- /dev/null
+++ b/compressor/tests/precompiler.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+from __future__ import with_statement
+import optparse
+import sys
+
+def main():
+ p = optparse.OptionParser()
+ p.add_option('-f', '--file', action="store",
+ type="string", dest="filename",
+ help="File to read from, defaults to stdin", default=None)
+ p.add_option('-o', '--output', action="store",
+ type="string", dest="outfile",
+ help="File to write to, defaults to stdout", default=None)
+
+ options, arguments = p.parse_args()
+
+ if options.filename:
+ f = open(options.filename)
+ content = f.read()
+ f.close()
+ else:
+ content = sys.stdin.read()
+
+ content = content.replace('background:', 'color:')
+
+ if options.outfile:
+ with open(options.outfile, 'w') as f:
+ f.write(content)
+ else:
+ print content
+
+
+if __name__ == '__main__':
+ main()
diff --git a/compressor/tests/settings.py b/compressor/tests/settings.py
new file mode 100644
index 0000000..5abf350
--- /dev/null
+++ b/compressor/tests/settings.py
@@ -0,0 +1,32 @@
+import os
+
+TEST_DIR = os.path.dirname(os.path.abspath(__file__))
+
+COMPRESS_CACHE_BACKEND = 'locmem://'
+
+DATABASE_ENGINE = 'sqlite3'
+
+INSTALLED_APPS = [
+ 'django.contrib.contenttypes',
+ 'django.contrib.sites',
+ 'django.contrib.auth',
+ 'django.contrib.admin',
+ 'compressor',
+ 'compressor.tests',
+ 'django_jenkins',
+]
+
+MEDIA_URL = '/media/'
+
+MEDIA_ROOT = os.path.join(TEST_DIR, 'media')
+
+TEMPLATE_DIRS = (
+ os.path.join(TEST_DIR, 'templates'),
+)
+
+JENKINS_TASKS = (
+ 'django_jenkins.tasks.run_pyflakes',
+ 'django_jenkins.tasks.run_pep8',
+ 'django_jenkins.tasks.with_coverage',
+ 'django_jenkins.tasks.django_tests',
+)
diff --git a/compressor/tests/templates/base.html b/compressor/tests/templates/base.html
new file mode 100644
index 0000000..a18ad5f
--- /dev/null
+++ b/compressor/tests/templates/base.html
@@ -0,0 +1,16 @@
+{% block content %}{% endblock %}
+
+{% block js%}
+
+{% endblock %}
+
+{% block css %}
+
+{% endblock %}
+
diff --git a/compressor/tests/templates/error_tests/buggy_extends.html b/compressor/tests/templates/error_tests/buggy_extends.html
new file mode 100644
index 0000000..e9ed6c6
--- /dev/null
+++ b/compressor/tests/templates/error_tests/buggy_extends.html
@@ -0,0 +1,10 @@
+{% extends "buggy_extends.html" %}
+{% load compress %}
+
+{% compress css %}
+
+{% endcompress %}
diff --git a/compressor/tests/templates/error_tests/buggy_template.html b/compressor/tests/templates/error_tests/buggy_template.html
new file mode 100644
index 0000000..1a99dab
--- /dev/null
+++ b/compressor/tests/templates/error_tests/buggy_template.html
@@ -0,0 +1,12 @@
+{% load compress %}
+
+{% compress css %}
+
+{% endcompress %}
+
+
+{% fail %}
diff --git a/compressor/tests/templates/error_tests/missing_extends.html b/compressor/tests/templates/error_tests/missing_extends.html
new file mode 100644
index 0000000..588ba8a
--- /dev/null
+++ b/compressor/tests/templates/error_tests/missing_extends.html
@@ -0,0 +1,10 @@
+{% extends "missing.html" %}
+{% load compress %}
+
+{% compress css %}
+
+{% endcompress %}
diff --git a/compressor/tests/templates/test_compressor_offline.html b/compressor/tests/templates/test_compressor_offline.html
new file mode 100644
index 0000000..378141d
--- /dev/null
+++ b/compressor/tests/templates/test_compressor_offline.html
@@ -0,0 +1,49 @@
+{% extends "base.html" %}
+{% load compress %}
+
+{% block content %}{% spaceless %}
+{% compress css%}
+
+{% endcompress %}
+
+{% compress js%}
+
+{% endcompress %}
+
+{% compress js%}
+
+{% endcompress %}
+
+{% if condition %}
+ {% compress js%}
+
+ {% endcompress %}
+{% endif %}
+
+{% endspaceless %}{% endblock %}
+
+{% block js %}{% spaceless %}
+ {% compress js %}
+ {{ block.super }}
+
+ {% endcompress %}
+{% endspaceless %}{% endblock %}
+
+{% block css %}{% spaceless %}
+ {% compress css %}
+ {{ block.super }}
+
+ {% endcompress %}
+{% endspaceless %}{% endblock %}
diff --git a/compressor/tests/tests/__init__.py b/compressor/tests/tests/__init__.py
new file mode 100644
index 0000000..dfaadab
--- /dev/null
+++ b/compressor/tests/tests/__init__.py
@@ -0,0 +1,12 @@
+from .base import (CompressorTestCase, CssMediaTestCase, VerboseTestCase,
+ CacheBackendTestCase)
+from .filters import (CssTidyTestCase, PrecompilerTestCase, CssMinTestCase,
+ CssAbsolutizingTestCase, CssAbsolutizingTestCaseWithHash,
+ CssDataUriTestCase)
+from .jinja2ext import TestJinja2CompressorExtension
+from .offline import OfflineGenerationTestCase
+from .parsers import (LxmlParserTests, Html5LibParserTests,
+ BeautifulSoupParserTests, HtmlParserTests)
+from .signals import PostCompressSignalTestCase
+from .storages import StorageTestCase
+from .templatetags import TemplatetagTestCase, PrecompilerTemplatetagTestCase
diff --git a/compressor/tests/tests/base.py b/compressor/tests/tests/base.py
new file mode 100644
index 0000000..097fabe
--- /dev/null
+++ b/compressor/tests/tests/base.py
@@ -0,0 +1,172 @@
+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.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''
+ 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 = """\
+
+
+"""
+ self.css_node = CssCompressor(self.css)
+
+ self.js = """\
+
+"""
+ self.js_node = JsCompressor(self.js)
+
+ def test_css_split(self):
+ out = [
+ (SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css', u'one.css'), u'css/one.css', u''),
+ (SOURCE_HUNK, u'p { border:5px solid green;}', None, u''),
+ (SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css', u'two.css'), u'css/two.css', u''),
+ ]
+ 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; }'
+ hunks = '\n'.join([h for 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', u'one.js'), u'js/one.js', ''),
+ (SOURCE_HUNK, u'obj.value = "value";', None, ''),
+ ]
+ 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_output(self):
+ out = u''
+ self.assertEqual(out, self.js_node.output())
+
+ def test_js_override_url(self):
+ self.js_node.context.update({'url': u'This is not a url, just a text'})
+ out = u''
+ self.assertEqual(out, self.js_node.output())
+
+ def test_css_override_url(self):
+ self.css_node.context.update({'url': u'This is not a url, just a text'})
+ output = css_tag('/media/CACHE/css/e41ba2cc6982.css')
+ self.assertEqual(output, self.css_node.output().strip())
+
+ 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''
+ 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''
+ self.assertEqual(output, JsCompressor(self.js).output())
+ settings.COMPRESS_OUTPUT_DIR = ''
+ output = u''
+ self.assertEqual(output, JsCompressor(self.js).output())
+ settings.COMPRESS_OUTPUT_DIR = '/custom/nested/'
+ output = u''
+ self.assertEqual(output, JsCompressor(self.js).output())
+ finally:
+ settings.COMPRESS_OUTPUT_DIR = old_output_dir
+
+
+class CssMediaTestCase(TestCase):
+ def setUp(self):
+ self.css = """\
+
+
+
+"""
+ 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 + ''
+ 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)
diff --git a/compressor/tests/tests/filters.py b/compressor/tests/tests/filters.py
new file mode 100644
index 0000000..92fd551
--- /dev/null
+++ b/compressor/tests/tests/filters.py
@@ -0,0 +1,201 @@
+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, get_hashed_content
+from compressor.conf import settings
+from compressor.css import CssCompressor
+from compressor.utils import find_command
+from compressor.filters.base import CompilerFilter
+from compressor.filters.cssmin import CSSMinFilter
+from compressor.filters.css_default import CssAbsoluteFilter
+
+from .base import 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 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; }%s" % os.linesep, 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; }%s" % os.linesep, 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; }%s" % os.linesep, compiler.input())
+
+
+class CssMinTestCase(TestCase):
+ def test_cssmin_filter(self):
+ 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):
+ hashing_method = 'mtime'
+ hashing_func = staticmethod(get_hashed_mtime)
+ content = "p { background: url('../../img/python.png') }"
+
+ def setUp(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ self.old_url = settings.COMPRESS_URL
+ self.old_hashing_method = settings.COMPRESS_CSS_HASHING_METHOD
+ settings.COMPRESS_ENABLED = True
+ settings.COMPRESS_URL = '/media/'
+ settings.COMPRESS_CSS_HASHING_METHOD = self.hashing_method
+ self.css = """
+
+
+ """
+ self.css_node = CssCompressor(self.css)
+
+ def tearDown(self):
+ settings.COMPRESS_ENABLED = self.old_enabled
+ settings.COMPRESS_URL = self.old_url
+ settings.COMPRESS_CSS_HASHING_METHOD = self.old_hashing_method
+
+ def test_css_absolute_filter(self):
+ filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
+ imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ filter = CssAbsoluteFilter(self.content)
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+ settings.COMPRESS_URL = 'http://media.example.com/'
+ filter = CssAbsoluteFilter(self.content)
+ filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+
+ def test_css_absolute_filter_https(self):
+ filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
+ imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ filter = CssAbsoluteFilter(self.content)
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+ settings.COMPRESS_URL = 'https://media.example.com/'
+ filter = CssAbsoluteFilter(self.content)
+ filename = os.path.join(settings.COMPRESS_ROOT, 'css/url/test.css')
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+
+ def test_css_absolute_filter_relative_path(self):
+ filename = os.path.join(settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css')
+ imagefilename = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ filter = CssAbsoluteFilter(self.content)
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+ settings.COMPRESS_URL = 'https://media.example.com/'
+ filter = CssAbsoluteFilter(self.content)
+ output = "p { background: url('%simg/python.png?%s') }" % (settings.COMPRESS_URL, self.hashing_func(imagefilename))
+ self.assertEqual(output, filter.input(filename=filename, basename='css/url/test.css'))
+
+ def test_css_hunks(self):
+ hash_dict = {
+ 'hash1': self.hashing_func(os.path.join(settings.COMPRESS_ROOT, 'img/python.png')),
+ 'hash2': self.hashing_func(os.path.join(settings.COMPRESS_ROOT, 'img/add.png')),
+ }
+ self.assertEqual([u"""\
+p { background: url('/media/img/python.png?%(hash1)s'); }
+p { background: url('/media/img/python.png?%(hash1)s'); }
+p { background: url('/media/img/python.png?%(hash1)s'); }
+p { background: url('/media/img/python.png?%(hash1)s'); }
+""" % hash_dict,
+ u"""\
+p { background: url('/media/img/add.png?%(hash2)s'); }
+p { background: url('/media/img/add.png?%(hash2)s'); }
+p { background: url('/media/img/add.png?%(hash2)s'); }
+p { background: url('/media/img/add.png?%(hash2)s'); }
+""" % hash_dict], list(self.css_node.hunks()))
+
+ def test_guess_filename(self):
+ for base_url in ('/media/', 'http://media.example.com/'):
+ settings.COMPRESS_URL = base_url
+ url = '%s/img/python.png' % settings.COMPRESS_URL.rstrip('/')
+ path = os.path.join(settings.COMPRESS_ROOT, 'img/python.png')
+ content = "p { background: url('%s') }" % url
+ filter = CssAbsoluteFilter(content)
+ self.assertEqual(path, filter.guess_filename(url))
+
+
+class CssAbsolutizingTestCaseWithHash(CssAbsolutizingTestCase):
+ hashing_method = 'content'
+ hashing_func = staticmethod(get_hashed_content)
+
+ def setUp(self):
+ super(CssAbsolutizingTestCaseWithHash, self).setUp()
+ self.css = """
+
+
+ """
+ self.css_node = CssCompressor(self.css)
+
+
+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/'
+ settings.COMPRESS_CSS_HASHING_METHOD = 'mtime'
+ self.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]
+ self.assertEqual(out, list(self.css_node.hunks()))
diff --git a/compressor/tests/tests/jinja2ext.py b/compressor/tests/tests/jinja2ext.py
new file mode 100644
index 0000000..f3fe028
--- /dev/null
+++ b/compressor/tests/tests/jinja2ext.py
@@ -0,0 +1,126 @@
+from __future__ import with_statement
+
+from django.test import TestCase
+
+import jinja2
+
+from compressor.conf import settings
+
+from .base import css_tag
+
+
+class TestJinja2CompressorExtension(TestCase):
+ """
+ Test case for jinja2 extension.
+
+ .. note::
+ At tests we need to make some extra care about whitespace. Please note
+ that we use jinja2 specific controls (*minus* character at block's
+ beginning or end). For more information see jinja2 documentation.
+ """
+
+ def assertStrippedEqual(self, result, expected):
+ self.assertEqual(result.strip(), expected.strip(), "%r != %r" % (
+ result.strip(), expected.strip()))
+
+ def setUp(self):
+ from compressor.contrib.jinja2ext import CompressorExtension
+ self.env = jinja2.Environment(extensions=[CompressorExtension])
+
+ def test_error_raised_if_no_arguments_given(self):
+ self.assertRaises(jinja2.exceptions.TemplateSyntaxError,
+ self.env.from_string, '{% compress %}Foobar{% endcompress %}')
+
+ def test_error_raised_if_wrong_kind_given(self):
+ self.assertRaises(jinja2.exceptions.TemplateSyntaxError,
+ self.env.from_string, '{% compress foo %}Foobar{% endcompress %}')
+
+ def test_error_raised_if_wrong_mode_given(self):
+ self.assertRaises(jinja2.exceptions.TemplateSyntaxError,
+ self.env.from_string, '{% compress css foo %}Foobar{% endcompress %}')
+
+ def test_compress_is_disabled(self):
+ org_COMPRESS_ENABLED = settings.COMPRESS_ENABLED
+ settings.COMPRESS_ENABLED = False
+ tag_body = '\n'.join([
+ '',
+ '',
+ '',
+ ])
+ template_string = '{% compress css %}' + tag_body + '{% endcompress %}'
+ template = self.env.from_string(template_string)
+ self.assertEqual(tag_body, template.render())
+ settings.COMPRESS_ENABLED = org_COMPRESS_ENABLED
+
+ def test_empty_tag(self):
+ template = self.env.from_string(u"""{% compress js %}{% block js %}
+ {% endblock %}{% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ self.assertEqual(u'', template.render(context))
+
+ def test_css_tag(self):
+ template = self.env.from_string(u"""{% compress css -%}
+
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = css_tag("/media/CACHE/css/e41ba2cc6982.css")
+ self.assertEqual(out, template.render(context))
+
+ def test_nonascii_css_tag(self):
+ template = self.env.from_string(u"""{% compress css -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = css_tag("/media/CACHE/css/799f6defe43c.css")
+ self.assertEqual(out, template.render(context))
+
+ def test_js_tag(self):
+ template = self.env.from_string(u"""{% compress js -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = u''
+ self.assertEqual(out, template.render(context))
+
+ def test_nonascii_js_tag(self):
+ template = self.env.from_string(u"""{% compress js -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = u''
+ self.assertEqual(out, template.render(context))
+
+ def test_nonascii_latin1_js_tag(self):
+ template = self.env.from_string(u"""{% compress js -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = u''
+ self.assertEqual(out, template.render(context))
+
+ def test_css_inline(self):
+ template = self.env.from_string(u"""{% compress css, inline -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = '\n'.join([
+ '',
+ ])
+ self.assertEqual(out, template.render(context))
+
+ def test_js_inline(self):
+ template = self.env.from_string(u"""{% compress js, inline -%}
+
+
+ {% endcompress %}""")
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = ''
+ self.assertEqual(out, template.render(context))
diff --git a/compressor/tests/tests/offline.py b/compressor/tests/tests/offline.py
new file mode 100644
index 0000000..685aed2
--- /dev/null
+++ b/compressor/tests/tests/offline.py
@@ -0,0 +1,93 @@
+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.exceptions import OfflineGenerationError
+from compressor.management.commands.compress import Command as CompressCommand
+from compressor.storage import default_storage
+
+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()
+ manifest_path = os.path.join('CACHE', 'manifest.json')
+ if default_storage.exists(manifest_path):
+ default_storage.delete(manifest_path)
+
+ def test_rendering_without_compressing_raises_exception(self):
+ self.assertRaises(OfflineGenerationError,
+ self.template.render, Context({}))
+
+ def test_requires_model_validation(self):
+ self.assertFalse(CompressCommand.requires_model_validation)
+
+ def test_offline(self):
+ count, result = CompressCommand().compress()
+ self.assertEqual(6, count)
+ self.assertEqual([
+ css_tag('/media/CACHE/css/cd579b7deb7d.css'),
+ u'',
+ u'',
+ u'',
+ u'',
+ u'',
+ ], result)
+
+ def test_offline_with_context(self):
+ self._old_offline_context = settings.COMPRESS_OFFLINE_CONTEXT
+ settings.COMPRESS_OFFLINE_CONTEXT = {
+ 'color': 'blue',
+ 'condition': 'red',
+ }
+ count, result = CompressCommand().compress()
+ self.assertEqual(6, count)
+ self.assertEqual([
+ css_tag('/media/CACHE/css/ee62fbfd116a.css'),
+ u'',
+ u'',
+ u'',
+ u'',
+ u'',
+ ], 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
diff --git a/compressor/tests/tests/parsers.py b/compressor/tests/tests/parsers.py
new file mode 100644
index 0000000..e04a28f
--- /dev/null
+++ b/compressor/tests/tests/parsers.py
@@ -0,0 +1,88 @@
+from __future__ import with_statement
+import os
+from unittest2 import skipIf
+
+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.base import SOURCE_HUNK, SOURCE_FILE
+from compressor.conf import settings
+from compressor.css import CssCompressor
+
+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 setUp(self):
+ super(Html5LibParserTests, self).setUp()
+ # special version of the css since the parser sucks
+ self.css = """\
+
+
+"""
+ self.css_node = CssCompressor(self.css)
+
+ def test_css_split(self):
+ out = [
+ (SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css', u'one.css'), u'css/one.css', u''),
+ (SOURCE_HUNK, u'p { border:5px solid green;}', None, u''),
+ (SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'css', u'two.css'), u'css/two.css', u''),
+ ]
+ 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', u'one.js'), u'js/one.js', u''),
+ (SOURCE_HUNK, u'obj.value = "value";', None, u''),
+ ]
+ 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'
+
diff --git a/compressor/tests/tests/signals.py b/compressor/tests/tests/signals.py
new file mode 100644
index 0000000..c9801d7
--- /dev/null
+++ b/compressor/tests/tests/signals.py
@@ -0,0 +1,67 @@
+from django.test import TestCase
+
+from mock import Mock
+
+from compressor.conf import settings
+from compressor.css import CssCompressor
+from compressor.js import JsCompressor
+from compressor.signals import post_compress
+
+
+class PostCompressSignalTestCase(TestCase):
+ def setUp(self):
+ settings.COMPRESS_ENABLED = True
+ settings.COMPRESS_PRECOMPILERS = {}
+ settings.COMPRESS_DEBUG_TOGGLE = 'nocompress'
+ self.css = """\
+
+
+"""
+ self.css_node = CssCompressor(self.css)
+
+ self.js = """\
+
+"""
+ self.js_node = JsCompressor(self.js)
+
+ def tearDown(self):
+ post_compress.disconnect()
+
+ def test_js_signal_sent(self):
+ def listener(sender, **kwargs):
+ pass
+ callback = Mock(wraps=listener)
+ post_compress.connect(callback)
+ self.js_node.output()
+ args, kwargs = callback.call_args
+ self.assertEquals(JsCompressor, kwargs['sender'])
+ self.assertEquals('js', kwargs['type'])
+ self.assertEquals('file', kwargs['mode'])
+ context = kwargs['context']
+ assert 'url' in context['compressed']
+
+ def test_css_signal_sent(self):
+ def listener(sender, **kwargs):
+ pass
+ callback = Mock(wraps=listener)
+ post_compress.connect(callback)
+ self.css_node.output()
+ args, kwargs = callback.call_args
+ self.assertEquals(CssCompressor, kwargs['sender'])
+ self.assertEquals('css', kwargs['type'])
+ self.assertEquals('file', kwargs['mode'])
+ context = kwargs['context']
+ assert 'url' in context['compressed']
+
+ def test_css_signal_multiple_media_attributes(self):
+ css = """\
+
+
+"""
+ css_node = CssCompressor(css)
+ def listener(sender, **kwargs):
+ pass
+ callback = Mock(wraps=listener)
+ post_compress.connect(callback)
+ css_node.output()
+ self.assertEquals(3, callback.call_count)
diff --git a/compressor/tests/tests/storages.py b/compressor/tests/tests/storages.py
new file mode 100644
index 0000000..ae43b85
--- /dev/null
+++ b/compressor/tests/tests/storages.py
@@ -0,0 +1,53 @@
+from __future__ import with_statement
+import errno
+import os
+
+from django.core.files.base import ContentFile
+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 %}
+
+
+
+ {% endcompress %}
+ """
+ context = {'MEDIA_URL': settings.COMPRESS_URL}
+ out = css_tag("/media/CACHE/css/1d4424458f88.css")
+ self.assertEqual(out, render(template, context))
+
+ def test_race_condition_handling(self):
+ # Hold on to original os.remove
+ original_remove = os.remove
+
+ def race_remove(path):
+ "Patched os.remove to raise ENOENT (No such file or directory)"
+ original_remove(path)
+ raise OSError(errno.ENOENT, u'Fake ENOENT')
+
+ try:
+ os.remove = race_remove
+ self._storage.save('race.file', ContentFile('Fake ENOENT'))
+ self._storage.delete('race.file')
+ self.assertFalse(self._storage.exists('race.file'))
+ finally:
+ # Restore os.remove
+ os.remove = original_remove
diff --git a/compressor/tests/tests/templatetags.py b/compressor/tests/tests/templatetags.py
new file mode 100644
index 0000000..ed31586
--- /dev/null
+++ b/compressor/tests/tests/templatetags.py
@@ -0,0 +1,238 @@
+from __future__ import with_statement
+
+import os
+import sys
+
+from mock import Mock
+
+from django.template import Template, Context, TemplateSyntaxError
+from django.test import TestCase
+
+from compressor.conf import settings
+from compressor.signals import post_compress
+
+from .base import css_tag, test_dir
+
+
+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 %}
+
+
+
+{% endcompress %}"""
+ out = css_tag("/media/CACHE/css/e41ba2cc6982.css")
+ self.assertEqual(out, render(template, self.context))
+
+ def test_uppercase_rel(self):
+ template = u"""{% load compress %}{% compress 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 %}
+
+
+ {% 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 %}
+
+
+ {% endcompress %}
+ """
+ out = u''
+ self.assertEqual(out, render(template, self.context))
+
+ def test_nonascii_js_tag(self):
+ template = u"""{% load compress %}{% compress js %}
+
+
+ {% endcompress %}
+ """
+ out = u''
+ self.assertEqual(out, render(template, self.context))
+
+ def test_nonascii_latin1_js_tag(self):
+ template = u"""{% load compress %}{% compress js %}
+
+
+ {% endcompress %}
+ """
+ out = u''
+ self.assertEqual(out, render(template, self.context))
+
+ def test_compress_tag_with_illegal_arguments(self):
+ template = u"""{% load compress %}{% compress pony %}
+
+ {% endcompress %}"""
+ self.assertRaises(TemplateSyntaxError, render, template, {})
+
+ def test_debug_toggle(self):
+ template = u"""{% load compress %}{% compress js %}
+
+
+ {% endcompress %}
+ """
+ class MockDebugRequest(object):
+ GET = {settings.COMPRESS_DEBUG_TOGGLE: 'true'}
+ context = dict(self.context, request=MockDebugRequest())
+ out = u"""
+ """
+ self.assertEqual(out, render(template, context))
+
+ def test_named_compress_tag(self):
+ template = u"""{% load compress %}{% compress js inline foo %}
+
+ {% endcompress %}
+ """
+ def listener(sender, **kwargs):
+ pass
+ callback = Mock(wraps=listener)
+ post_compress.connect(callback)
+ render(template)
+ args, kwargs = callback.call_args
+ context = kwargs['context']
+ self.assertEqual('foo', context['compressed']['name'])
+
+
+class PrecompilerTemplatetagTestCase(TestCase):
+ def setUp(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ self.old_precompilers = settings.COMPRESS_PRECOMPILERS
+
+ precompiler = os.path.join(test_dir, 'precompiler.py')
+ python = sys.executable
+
+ settings.COMPRESS_ENABLED = True
+ settings.COMPRESS_PRECOMPILERS = (
+ ('text/coffeescript', '%s %s' % (python, precompiler)),
+ )
+ self.context = {'MEDIA_URL': settings.COMPRESS_URL}
+
+ def tearDown(self):
+ settings.COMPRESS_ENABLED = self.old_enabled
+ settings.COMPRESS_PRECOMPILERS = self.old_precompilers
+
+ def test_compress_coffeescript_tag(self):
+ template = u"""{% load compress %}{% compress js %}
+
+ {% endcompress %}"""
+ out = script(src="/media/CACHE/js/e920d58f166d.js")
+ self.assertEqual(out, render(template, self.context))
+
+ def test_compress_coffeescript_tag_and_javascript_tag(self):
+ template = u"""{% load compress %}{% compress js %}
+
+
+ {% endcompress %}"""
+ out = script(src="/media/CACHE/js/ef6b32a54575.js")
+ self.assertEqual(out, render(template, self.context))
+
+ def test_coffeescript_and_js_tag_with_compress_enabled_equals_false(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ settings.COMPRESS_ENABLED = False
+ try:
+ template = u"""{% load compress %}{% compress js %}
+
+
+ {% endcompress %}"""
+ out = (script('# this is a comment.\n') + '\n' +
+ script('# this too is a comment.'))
+ self.assertEqual(out, render(template, self.context))
+ finally:
+ settings.COMPRESS_ENABLED = self.old_enabled
+
+ def test_compress_coffeescript_tag_compress_enabled_is_false(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ settings.COMPRESS_ENABLED = False
+ try:
+ template = u"""{% load compress %}{% compress js %}
+
+ {% endcompress %}"""
+ out = script("# this is a comment.\n")
+ self.assertEqual(out, render(template, self.context))
+ finally:
+ settings.COMPRESS_ENABLED = self.old_enabled
+
+ def test_compress_coffeescript_file_tag_compress_enabled_is_false(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ settings.COMPRESS_ENABLED = False
+ try:
+ template = u"""
+ {% load compress %}{% compress js %}
+
+ {% endcompress %}"""
+
+ out = script(src="/media/CACHE/js/one.95cfb869eead.js")
+ self.assertEqual(out, render(template, self.context))
+ finally:
+ settings.COMPRESS_ENABLED = self.old_enabled
+
+ def test_multiple_file_order_conserved(self):
+ self.old_enabled = settings.COMPRESS_ENABLED
+ settings.COMPRESS_ENABLED = False
+ try:
+ template = u"""
+ {% load compress %}{% compress js %}
+
+
+
+ {% endcompress %}"""
+
+ out = '\n'.join([
+ script(src="/media/CACHE/js/one.95cfb869eead.js"),
+ script(scripttype="", src="/media/js/one.js"),
+ script(src="/media/CACHE/js/one.81a2cd965815.js"),])
+
+ self.assertEqual(out, render(template, self.context))
+ finally:
+ settings.COMPRESS_ENABLED = self.old_enabled
+
+def script(content="", src="", scripttype="text/javascript"):
+ """
+ returns a unicode text html script element.
+
+ >>> script('#this is a comment', scripttype="text/applescript")
+ ''
+ """
+ out_script = u'' % content
diff --git a/setup.py b/setup.py
index 1e62f31..f2b9589 100644
--- a/setup.py
+++ b/setup.py
@@ -107,7 +107,7 @@ setup(
long_description = read('README.rst'),
author = 'Jannis Leidel',
author_email = 'jannis@leidel.info',
- packages = find_packages(exclude=['tests', 'tests.*']),
+ packages = find_packages(),
package_data = find_package_data('compressor', only_in_packages=False),
classifiers = [
'Development Status :: 5 - Production/Stable',
diff --git a/tox.ini b/tox.ini
index a3386b8..01b106b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,11 +1,9 @@
[testenv]
+downloadcache = {toxworkdir}/_download/
+setenv =
+ DJANGO_SETTINGS_MODULE = compressor.tests.settings
commands =
{envbindir}/python {envbindir}/django-admin.py jenkins {posargs:tests}
-downloadcache = {toxworkdir}/_download/
-distribute = false
-setenv =
- PYTHONPATH = {toxinidir}
- DJANGO_SETTINGS_MODULE = tests.settings
[testenv:docs]
basepython = python2.7