import os, re import gzip from django.template import Template, Context, TemplateSyntaxError from django.test import TestCase from compressor import CssCompressor, JsCompressor from compressor.conf import settings from compressor.storage import CompressorFileStorage from django.conf import settings as django_settings from BeautifulSoup import BeautifulSoup class CompressorTestCase(TestCase): def setUp(self): settings.COMPRESS = True self.css = """ """ self.cssNode = CssCompressor(self.css) self.js = """ """ self.jsNode = JsCompressor(self.js) def test_css_split(self): out = [ ('file', os.path.join(settings.MEDIA_ROOT, u'css/one.css'), u''), ('hunk', u'p { border:5px solid green;}', u''), ('file', os.path.join(settings.MEDIA_ROOT, u'css/two.css'), u''), ] split = self.cssNode.split_contents() split = [(x[0], x[1], self.cssNode.parser.elem_str(x[2])) 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, self.cssNode.hunks) def test_css_output(self): out = u'body { background:#990; }\np { border:5px solid green;}\nbody { color:#fff; }' self.assertEqual(out, self.cssNode.combined) def test_css_mtimes(self): is_date = re.compile(r'^\d{10}\.\d$') for date in self.cssNode.mtimes: self.assert_(is_date.match(str(date)), "mtimes is returning something that doesn't look like a date") def test_css_return_if_off(self): settings.COMPRESS = False self.assertEqual(self.css, self.cssNode.output()) def test_cachekey(self): is_cachekey = re.compile(r'django_compressor\.\w{12}') self.assert_(is_cachekey.match(self.cssNode.cachekey), "cachekey is returning something that doesn't look like r'django_compressor\.\w{12}'") def test_css_hash(self): self.assertEqual('f7c661b7a124', self.cssNode.hash) def test_css_return_if_on(self): output = u'' self.assertEqual(output, self.cssNode.output().strip()) def test_js_split(self): out = [('file', os.path.join(settings.MEDIA_ROOT, u'js/one.js'), ''), ('hunk', u'obj.value = "value";', '') ] split = self.jsNode.split_contents() split = [(x[0], x[1], self.jsNode.parser.elem_str(x[2])) for x in split] self.assertEqual(out, split) def test_js_hunks(self): out = ['obj = {};', u'obj.value = "value";'] self.assertEqual(out, self.jsNode.hunks) def test_js_concat(self): out = u'obj = {};\nobj.value = "value";' self.assertEqual(out, self.jsNode.concat()) def test_js_output(self): out = u'obj={};obj.value="value";' self.assertEqual(out, self.jsNode.combined) def test_js_return_if_off(self): settings.COMPRESS = False self.assertEqual(self.js, self.jsNode.output()) def test_js_return_if_on(self): output = u'' self.assertEqual(output, self.jsNode.output()) def test_custom_output_dir(self): old_output_dir = settings.OUTPUT_DIR settings.OUTPUT_DIR = 'custom' output = u'' self.assertEqual(output, JsCompressor(self.js).output()) settings.OUTPUT_DIR = '' output = u'' self.assertEqual(output, JsCompressor(self.js).output()) settings.OUTPUT_DIR = '/custom/nested/' output = u'' self.assertEqual(output, JsCompressor(self.js).output()) settings.OUTPUT_DIR = old_output_dir class LxmlCompressorTestCase(CompressorTestCase): def test_css_split(self): out = [ ('file', os.path.join(settings.MEDIA_ROOT, u'css/one.css'), u''), ('hunk', u'p { border:5px solid green;}', u''), ('file', os.path.join(settings.MEDIA_ROOT, u'css/two.css'), u''), ] split = self.cssNode.split_contents() split = [(x[0], x[1], self.cssNode.parser.elem_str(x[2])) for x in split] self.assertEqual(out, split) def setUp(self): self.old_parser = settings.PARSER settings.PARSER = 'compressor.parser.LxmlParser' super(LxmlCompressorTestCase, self).setUp() def tearDown(self): settings.PARSER = self.old_parser class CssAbsolutizingTestCase(TestCase): def setUp(self): settings.COMPRESS = True settings.MEDIA_URL = '/media/' self.css = """ """ self.cssNode = CssCompressor(self.css) def test_css_absolute_filter(self): from compressor.filters.css_default import CssAbsoluteFilter filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') content = "p { background: url('../../images/image.gif') }" output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL filter = CssAbsoluteFilter(content) self.assertEqual(output, filter.input(filename=filename)) settings.MEDIA_URL = 'http://media.example.com/' filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL self.assertEqual(output, filter.input(filename=filename)) def test_css_absolute_filter_https(self): from compressor.filters.css_default import CssAbsoluteFilter filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') content = "p { background: url('../../images/image.gif') }" output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL filter = CssAbsoluteFilter(content) self.assertEqual(output, filter.input(filename=filename)) settings.MEDIA_URL = 'https://media.example.com/' filename = os.path.join(settings.MEDIA_ROOT, 'css/url/test.css') output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL self.assertEqual(output, filter.input(filename=filename)) def test_css_absolute_filter_relative_path(self): from compressor.filters.css_default import CssAbsoluteFilter filename = os.path.join(django_settings.TEST_DIR, 'whatever', '..', 'media', 'whatever/../css/url/test.css') content = "p { background: url('../../images/image.gif') }" output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL filter = CssAbsoluteFilter(content) self.assertEqual(output, filter.input(filename=filename)) settings.MEDIA_URL = 'https://media.example.com/' output = "p { background: url('%simages/image.gif?275088b9bcf0') }" % settings.MEDIA_URL self.assertEqual(output, filter.input(filename=filename)) def test_css_hunks(self): out = [u"p { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\n", u"p { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\np { background: url('/media/images/test.png?86ea855a37ed'); }\n", ] self.assertEqual(out, self.cssNode.hunks) class CssDataUriTestCase(TestCase): def setUp(self): settings.COMPRESS = True settings.COMPRESS_CSS_FILTERS = ['compressor.filters.datauri.CssDataUriFilter'] settings.MEDIA_URL = '/media/' self.css = """ """ self.cssNode = CssCompressor(self.css) def test_data_uris(self): 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?f59031cef54b"); }\n.datauri { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0 vr4MkhoXe0rZigAAAABJRU5ErkJggg=="); }\n'] self.assertEqual(out, self.cssNode.hunks) class CssMediaTestCase(TestCase): def setUp(self): self.css = """ """ self.cssNode = CssCompressor(self.css) def test_css_output(self): links = BeautifulSoup(self.cssNode.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 CssMinTestCase(TestCase): def test_cssmin_filter(self): from compressor.filters.cssmin import CSSMinFilter content = """p { background: rgb(51,102,153) url('../../images/image.gif'); } """ output = "p{background:#369 url('../../images/image.gif')}" self.assertEqual(output, CSSMinFilter(content).output()) def render(template_string, context_dict=None): """A shortcut for testing template output.""" if context_dict is None: context_dict = {} c = Context(context_dict) t = Template(template_string) return t.render(c).strip() class TemplatetagTestCase(TestCase): def setUp(self): settings.COMPRESS = True def test_empty_tag(self): template = u"""{% load compress %}{% compress js %}{% block js %} {% endblock %}{% endcompress %}""" context = { 'MEDIA_URL': settings.MEDIA_URL } self.assertEqual(u'', render(template, context)) def test_css_tag(self): template = u"""{% load compress %}{% compress css %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = u'' self.assertEqual(out, render(template, context)) def test_nonascii_css_tag(self): template = u"""{% load compress %}{% compress css %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = '' self.assertEqual(out, render(template, context)) def test_js_tag(self): template = u"""{% load compress %}{% compress js %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = u'' self.assertEqual(out, render(template, context)) def test_nonascii_js_tag(self): template = u"""{% load compress %}{% compress js %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = u'' self.assertEqual(out, render(template, context)) def test_nonascii_latin1_js_tag(self): template = u"""{% load compress %}{% compress js %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = u'' self.assertEqual(out, render(template, context)) def test_compress_tag_with_illegal_arguments(self): template = u"""{% load compress %}{% compress pony %} {% endcompress %}""" self.assertRaises(TemplateSyntaxError, render, template, {}) class TestStorage(CompressorFileStorage): """ Test compressor storage that gzips storage files """ def url(self, name): return u'%s.gz' % super(TestStorage, self).url(name) def save(self, filename, content): filename = super(TestStorage, self).save(filename, content) out = gzip.open(u'%s.gz' % self.path(filename), 'wb') out.writelines(open(self.path(filename), 'rb')) out.close() class StorageTestCase(TestCase): def setUp(self): self._storage = settings.STORAGE settings.STORAGE = 'core.tests.TestStorage' settings.COMPRESS = True def tearDown(self): settings.STORAGE = self._storage def test_css_tag_with_storage(self): template = u"""{% load compress %}{% compress css %} {% endcompress %} """ context = { 'MEDIA_URL': settings.MEDIA_URL } out = u'' self.assertEqual(out, render(template, context))