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