def test_concatenate_and_rewrite(self): compressor = Compressor() css = compressor.concatenate_and_rewrite([ os.path.join(settings.PIPELINE_ROOT, 'css/first.css'), os.path.join(settings.PIPELINE_ROOT, 'css/second.css') ]) self.assertEquals(""".concat {\n display: none;\n}\n.concatenate {\n display: block;\n}""", css)
def test_concatenate(self): compressor = Compressor() js = compressor.concatenate([ os.path.join(settings.PIPELINE_ROOT, 'js/first.js'), os.path.join(settings.PIPELINE_ROOT, 'js/second.js') ]) self.assertEquals("""(function() { function concat() {\n console.log(arguments);\n}\nfunction cat() {\n console.log("hello world");\n} }).call(this);""", js)
def test_url_rewrite(self): compressor = Compressor() output = compressor.concatenate_and_rewrite([ os.path.join(settings.PIPELINE_ROOT, 'css/urls.css'), ]) self.assertEquals(""".relative-url { background-image: url(http://localhost/static/images/sprite-buttons.png); } .absolute-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output)
class Packager(object): def __init__(self, storage=default_storage, verbose=False, css_packages=None, js_packages=None): self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = {"css": self.create_packages(css_packages), "js": self.create_packages(js_packages)} def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound("No corresponding package for %s package name : %s" % (kind, package_name)) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack( package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs ) def compile(self, paths): return self.compiler.compile(paths) def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print "Saving: %s" % output_filename paths = self.compile(package.paths) content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
def __init__(self, force=False, verbose=False): self.force = force self.verbose = verbose self.compressor = Compressor(verbose) self.versioning = Versioning(verbose) self.compiler = Compiler(verbose) self.packages = { 'css': self.create_packages(settings.PIPELINE_CSS), 'js': self.create_packages(settings.PIPELINE_JS), }
def __init__(self, storage=default_storage, verbose=False, css_packages=None, js_packages=None): self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = {"css": self.create_packages(css_packages), "js": self.create_packages(js_packages)}
def __init__(self, verbose=False, css_packages=None, js_packages=None): self.verbose = verbose self.compressor = Compressor(verbose) self.versioning = Versioning(verbose) self.compiler = Compiler(verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), }
def __init__(self, storage=None, verbose=False, css_packages=None, js_packages=None): if storage is None: storage = staticfiles_storage self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(storage=storage, verbose=verbose) if css_packages is None: css_packages = settings.STYLESHEETS if js_packages is None: js_packages = settings.JAVASCRIPT self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), }
def setUp(self): self.compressor = Compressor()
def setUp(self): self.maxDiff = None self.compressor = Compressor()
class CompressorImplementationTest(TestCase): maxDiff = None def setUp(self): self.compressor = Compressor() default_collector.collect(RequestFactory().get('/')) def tearDown(self): default_collector.clear() def _test_compressor(self, compressor_cls, compress_type, expected_file): override_settings = { ("%s_COMPRESSOR" % compress_type.upper()): compressor_cls, } with pipeline_settings(**override_settings): if compress_type == 'js': result = self.compressor.compress_js( [_('pipeline/js/first.js'), _('pipeline/js/second.js')]) else: result = self.compressor.compress_css( [_('pipeline/css/first.css'), _('pipeline/css/second.css')], os.path.join('pipeline', 'css', os.path.basename(expected_file))) with self.compressor.storage.open(expected_file, 'r') as f: expected = f.read() self.assertEqual(result, expected) def test_jsmin(self): self._test_compressor('pipeline.compressors.jsmin.JSMinCompressor', 'js', 'pipeline/compressors/jsmin.js') def test_slimit(self): self._test_compressor('pipeline.compressors.slimit.SlimItCompressor', 'js', 'pipeline/compressors/slimit.js') @skipUnless(settings.HAS_NODE, "requires node") def test_uglifyjs(self): self._test_compressor('pipeline.compressors.uglifyjs.UglifyJSCompressor', 'js', 'pipeline/compressors/uglifyjs.js') @skipUnless(settings.HAS_NODE, "requires node") def test_yuglify_js(self): self._test_compressor('pipeline.compressors.yuglify.YuglifyCompressor', 'js', 'pipeline/compressors/yuglify.js') @skipUnless(settings.HAS_NODE, "requires node") def test_yuglify_css(self): self._test_compressor('pipeline.compressors.yuglify.YuglifyCompressor', 'css', 'pipeline/compressors/yuglify.css') @skipUnless(settings.HAS_NODE, "requires node") def test_cssmin(self): self._test_compressor('pipeline.compressors.cssmin.CSSMinCompressor', 'css', 'pipeline/compressors/cssmin.css') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_closure(self): self._test_compressor('pipeline.compressors.closure.ClosureCompressor', 'js', 'pipeline/compressors/closure.js') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_yui_js(self): self._test_compressor('pipeline.compressors.yui.YUICompressor', 'js', 'pipeline/compressors/yui.js') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_yui_css(self): self._test_compressor('pipeline.compressors.yui.YUICompressor', 'css', 'pipeline/compressors/yui.css') @skipUnless(settings.HAS_CSSTIDY, "requires csstidy") def test_csstidy(self): self._test_compressor('pipeline.compressors.csstidy.CSSTidyCompressor', 'css', 'pipeline/compressors/csstidy.css')
class Packager(object): def __init__(self, storage=None, verbose=False, css_packages=None, js_packages=None): if storage is None: storage = staticfiles_storage self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(storage=storage, verbose=verbose) if css_packages is None: css_packages = settings.STYLESHEETS if js_packages is None: js_packages = settings.JAVASCRIPT self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % (kind, package_name)) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, compiler_options={}, force=False): return self.compiler.compile( paths, compiler_options=compiler_options, force=force, ) def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print("Saving: %s" % output_filename) paths = self.compile( package.paths, compiler_options=package.compiler_options, force=True, ) output_path = self.storage.path(output_filename) output_mtime = self.storage.get_modified_time(output_path) if any([ self.storage.get_modified_time(self.storage.path(path)) >= output_mtime for path in paths ]): content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_bytes(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite( [_('pipeline/css/first.css'), _('pipeline/css/second.css')], 'css/screen.css') self.assertEqual( """.concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate( [_('pipeline/js/first.js'), _('pipeline/js/second.js')]) self.assertEqual( """function concat() {\n console.log(arguments);\n}\n\nfunction cat() {\n console.log("hello world");\n}\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path( [_('js/templates/form.jst'), _('js/templates/field.jst')]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path( '../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') def test_compile_templates(self): templates = self.compressor.compile_templates( [_('pipeline/templates/photo/list.jst')]) self.assertEqual( templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual( templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse( self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse( self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue( self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse( self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path( "../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path( "/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual( """@font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output) def test_compressor_subprocess_unicode(self): tests_path = os.path.dirname(os.path.dirname(__file__)) output = SubProcessCompressor(False).execute_command( '/usr/bin/env cat', open(tests_path + '/assets/css/unicode.css').read()) self.assertEqual( """.some_class { // Some unicode content: "áéíóú"; } """, output)
def setUp(self): self.compressor = Compressor() self.old_pipeline_url = settings.PIPELINE_URL self.old_pipeline_root = settings.PIPELINE_ROOT settings.PIPELINE_URL = 'http://localhost/static/'
class CompressorTest(TestCase): def setUp(self): self.compressor = Compressor() self.old_pipeline_url = settings.PIPELINE_URL self.old_pipeline_root = settings.PIPELINE_ROOT settings.PIPELINE_URL = 'http://localhost/static/' def test_js_compressor_class(self): self.assertEquals(self.compressor.js_compressor, YUICompressor) def test_css_compressor_class(self): self.assertEquals(self.compressor.css_compressor, YUICompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ 'css/first.css', 'css/second.css' ]) self.assertEquals(""".concat {\n display: none;\n}\n.concatenate {\n display: block;\n}""", css) def test_concatenate(self): js = self.compressor.concatenate([ 'js/first.js', 'js/second.js' ]) self.assertEquals("""function concat() {\n console.log(arguments);\n}\nfunction cat() {\n console.log("hello world");\n}""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content('images/arrow.png') self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content('images/arrow.png') self.assertFalse(mock.called) def test_relative_path(self): settings.PIPELINE_ROOT = '/var/www/static/' relative_path = self.compressor.relative_path('/var/www/static/images/sprite.png') self.assertEquals(relative_path, '/images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path([ 'js/templates/form.jst', 'js/templates/field.jst' ]) self.assertEquals(base_path, 'js/templates') def test_absolute_path(self): absolute_path = self.compressor.absolute_path('../../images/sprite.png', 'css/plugins/') self.assertEquals(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEquals(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEquals(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEquals(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEquals(name, 'photo_detail') def test_compile_templates(self): templates = self.compressor.compile_templates(['templates/photo/list.jst']) self.assertEquals(templates, """window.JST = window.JST || {};\nwindow.JST['list'] = _.template('<div class="photo"> <img src="<%= src %>" /> <div class="caption"> <%= caption %> </div></div>');\n""") templates = self.compressor.compile_templates([ 'templates/video/detail.jst', 'templates/photo/detail.jst' ]) self.assertEqual(templates, """window.JST = window.JST || {};\nwindow.JST['video_detail'] = _.template('<div class="video"> <video src="<%= src %>" /> <div class="caption"> <%= description %> </div></div>');\nwindow.JST[\'photo_detail\'] = _.template(\'<div class="photo"> <img src="<%= src %>" /> <div class="caption"> <%= caption %> by <%= author %> </div></div>\');\n""") def test_embeddable(self): self.assertFalse(self.compressor.embeddable('images/sprite.png', None)) self.assertFalse(self.compressor.embeddable('images/arrow.png', 'datauri')) self.assertTrue(self.compressor.embeddable('images/embed/arrow.png', 'datauri')) self.assertFalse(self.compressor.embeddable('images/arrow.dat', 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css") self.assertEquals(asset_path, "http://localhost/static/images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css") self.assertEquals(asset_path, "http://localhost/static/images/sprite.png") @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") def test_construct_asset_path_windows(self): asset_path = self.compressor.construct_asset_path("\image\sprite.png", "css\plugins\gallery.css") self.assertEquals(asset_path, "http://localhost/static/images/sprite.png") def test_construct_asset_path_relative(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css", absolute_asset_paths=False) self.assertEquals(asset_path, "../../images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css", absolute_asset_paths=False) self.assertEquals(asset_path, "/images/sprite.png") def test_url_rewrite(self): self.maxDiff = None output = self.compressor.concatenate_and_rewrite([ 'css/urls.css', ]) self.assertMultiLineEqual("""@font-face { font-family: 'Pipeline'; src: url(http://localhost/static/fonts/pipeline.eot); src: url(http://localhost/static/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(http://localhost/static/fonts/pipeline.woff) format('woff'), url(http://localhost/static/fonts/pipeline.ttf) format('truetype'), url(http://localhost/static/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(http://localhost/static/images/sprite-buttons.png); } .absolute-url { background-image: url(http://localhost/static/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output) def tearDown(self): settings.PIPELINE_URL = self.old_pipeline_url settings.PIPELINE_ROOT = self.old_pipeline_root
class CompressorTest(TestCase): def setUp(self): self.compressor = Compressor() self.old_pipeline_url = settings.PIPELINE_URL self.old_pipeline_root = settings.PIPELINE_ROOT settings.PIPELINE_URL = 'http://localhost/static/' def test_js_compressor_class(self): self.assertEquals(self.compressor.js_compressor, YUICompressor) def test_css_compressor_class(self): self.assertEquals(self.compressor.css_compressor, YUICompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ 'css/first.css', 'css/second.css' ]) self.assertEquals(""".concat {\n display: none;\n}\n.concatenate {\n display: block;\n}""", css) def test_concatenate(self): js = self.compressor.concatenate([ 'js/first.js', 'js/second.js' ]) self.assertEquals("""function concat() {\n console.log(arguments);\n}\nfunction cat() {\n console.log("hello world");\n}""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content('images/arrow.png') self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content('images/arrow.png') self.assertFalse(mock.called) def test_relative_path(self): settings.PIPELINE_ROOT = '/var/www/static/' relative_path = self.compressor.relative_path('/var/www/static/images/sprite.png') self.assertEquals(relative_path, '/images/sprite.png') def test_absolute_path(self): absolute_path = self.compressor.absolute_path('../../images/sprite.png', 'css/plugins/gallery.css') self.assertEquals(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/gallery.css') self.assertEquals(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEquals(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEquals(name, 'photo_edit') def test_embeddable(self): self.assertFalse(self.compressor.embeddable('images/sprite.png', None)) self.assertFalse(self.compressor.embeddable('images/arrow.png', 'datauri')) self.assertTrue(self.compressor.embeddable('images/embed/arrow.png', 'datauri')) self.assertFalse(self.compressor.embeddable('images/arrow.dat', 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css") self.assertEquals(asset_path, "http://localhost/static/images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css") self.assertEquals(asset_path, "http://localhost/static/images/sprite.png") def test_url_rewrite(self): self.maxDiff = None output = self.compressor.concatenate_and_rewrite([ 'css/urls.css', ]) self.assertMultiLineEqual("""@font-face { font-family: 'Pipeline'; src: url(http://localhost/static/fonts/pipeline.eot); src: url(http://localhost/static/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(http://localhost/static/fonts/pipeline.woff) format('woff'), url(http://localhost/static/fonts/pipeline.ttf) format('truetype'), url(http://localhost/static/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(http://localhost/static/images/sprite-buttons.png); } .absolute-url { background-image: url(http://localhost/static/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output) def tearDown(self): settings.PIPELINE_URL = self.old_pipeline_url settings.PIPELINE_ROOT = self.old_pipeline_root
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ _('pipeline/css/first.css'), _('pipeline/css/second.css') ], 'css/screen.css') self.assertEqual(""".concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate([ _('pipeline/js/first.js'), _('pipeline/js/second.js') ]) self.assertEqual("""function concat() {\n console.log(arguments);\n}\n\nfunction cat() {\n console.log("hello world");\n}\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path([ _('js/templates/form.jst'), _('js/templates/field.jst') ]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path('../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') @override_settings(PIPELINE_TEMPLATE_SEPARATOR='/') def test_template_name_separator(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo/detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo/detail') def test_compile_templates(self): templates = self.compressor.compile_templates([_('pipeline/templates/photo/list.jst')]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse(self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue(self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual("""@font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); } """, output) def test_url_rewrite_data_uri(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/nested/nested.css'), ], 'pipeline/screen.css') self.assertEqual(""".data-url { background-image: url(data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E); } """, output) def test_compressor_subprocess_unicode(self): tests_path = os.path.dirname(os.path.dirname(__file__)) output = SubProcessCompressor(False).execute_command( '/usr/bin/env cat', open(tests_path + '/assets/css/unicode.css').read()) self.assertEqual(""".some_class { // Some unicode content: "áéíóú"; } """, output)
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ _('pipeline/css/first.css'), _('pipeline/css/second.css') ], 'css/screen.css') self.assertEqual(""".concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate([ _('pipeline/js/first.js'), _('pipeline/js/second.js') ]) self.assertEqual("""function concat() {\n console.log(arguments);\n}\n\nfunction cat() {\n console.log("hello world");\n}\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path([ _('js/templates/form.jst'), _('js/templates/field.jst') ]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path('../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') def test_compile_templates(self): templates = self.compressor.compile_templates([_('pipeline/templates/photo/list.jst')]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse(self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue(self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_compress_js(self): with patch.object(self.compressor.js_compressor, 'compress_js') as mock_method: paths = [] mock_method.return_value = 'asdf' (js, source_map) = self.compressor.compress_js(paths) self.assertEqual(js, 'asdf') self.assertEqual(source_map, '') mock_method.assert_called_with(u'(function() { }).call(this);') @patch('pipeline.compressors.yuglify.YuglifyCompressor') def test_compress_js_with_source_map(self, mock_constructor): mock_js_compressor = MagicMock() mock_constructor.return_value = mock_js_compressor mock_js_compressor.compress_js_with_source_map.return_value = ['code', 'map'] paths = [ _('pipeline/js/first.js'), _('pipeline/js/second.js') ] (js, source_map) = self.compressor.compress_js(paths, source_map_filename='map.js') self.assertEqual(js, 'code') self.assertEqual(source_map, 'map') call = mock_js_compressor.compress_js_with_source_map.call_args_list[0] call_args = call[0] self.assertRegexpMatches(call_args[0][0], 'first.js') self.assertRegexpMatches(call_args[0][1], 'second.js') self.assertEquals(call_args[1], 'map.js') self.assertEquals(call_args[2], '/static/') self.assertEquals(call_args[3], 'tests/static/') # Uncomment if you need a fully working version # May also need to tweak pipeline/conf/settings.py to point to real uglify binary # @patch('pipeline.compressors.yuglify.YuglifyCompressor') # def test_compress_js_with_source_map_real(self, mock_constructor): # mock_constructor.return_value = UglifyJSCompressor(False) # paths = [ # _('pipeline/js/first.js'), # _('pipeline/js/second.js') # ] # (js, source_map) = self.compressor.compress_js(paths, source_map_filename='wakawaka.js') # self.assertRegexpMatches(js, 'function concat.*function cat') # self.assertRegexpMatches(js, '@ sourceMappingURL=/static/wakawaka.js') # Bunch of newlines..easier to do 2 asserts # self.assertTrue(len(source_map) > 0) @patch('pipeline.compressors.yuglify.YuglifyCompressor') def test_compress_js_with_source_map_on_non_compatible_compressor(self, mock_constructor): mock_js_compressor = MagicMock() mock_constructor.return_value = mock_js_compressor del mock_js_compressor.compress_js_with_source_map with self.assertRaisesRegexp(CompressorError, 'cannot make source maps'): self.compressor.compress_js([], source_map_filename='map.js') @patch('pipeline.compressors.yuglify.YuglifyCompressor') def test_compress_js_with_source_map_and_templates(self, mock_constructor): mock_js_compressor = MagicMock() mock_constructor.return_value = mock_js_compressor with self.assertRaisesRegexp(CompressorError, 'Templates cannot be part of a group'): self.compressor.compress_js([], source_map_filename='map.js', templates=['foo.jst']) def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual("""@font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output)
class Packager(object): def __init__(self, storage=default_storage, verbose=False, css_packages=None, js_packages=None): self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % (kind, package_name)) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, force=False): return self.compiler.compile(paths, force=force) def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print("Saving: %s" % output_filename) paths = self.compile(package.paths, force=True) content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
class Packager(object): def __init__(self, storage=None, verbose=False, css_packages=None, js_packages=None): if storage is None: storage = staticfiles_storage self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(storage=storage, verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % ( kind, package_name ) ) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, force=False): paths = self.compiler.compile(paths, force=force) for path in paths: if not self.storage.exists(path): if self.verbose: print("Compiled file '%s' cannot be found with packager's storage. Locating it." % path) source_storage = self.find_source_storage(path) if source_storage is not None: with source_storage.open(path) as source_file: if self.verbose: print("Saving: %s" % path) self.storage.save(path, source_file) else: raise IOError("File does not exist: %s" % path) return paths def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print("Saving: %s" % output_filename) paths = self.compile(package.paths, force=True) content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_bytes(content))) def find_source_storage(self, path): for finder in get_finders(): for short_path, storage in finder.list(''): if short_path == path: if self.verbose: print("Found storage: %s" % str(self.storage)) return storage return None def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
class Packager(object): def __init__(self, verbose=False, css_packages=None, js_packages=None): self.verbose = verbose self.compressor = Compressor(verbose) self.versioning = Versioning(verbose) self.compiler = Compiler(verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name].copy() except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % (kind, package_name)) def individual_url(self, filename): relative_path = self.compressor.relative_path(filename)[1:] return urlparse.urljoin(settings.PIPELINE_URL, filepath_to_uri(relative_path)) def pack_stylesheets(self, package, **kwargs): variant = package.get('variant', None) absolute_asset_paths = package.get('absolute_asset_paths', True) return self.pack(package, self.compressor.compress_css, css_compressed, variant=variant, absolute_asset_paths=absolute_asset_paths, **kwargs) def compile(self, paths): return self.compiler.compile(paths) def pack(self, package, compress, signal, sync=False, force=False, **kwargs): if settings.PIPELINE_AUTO or (force and sync): need_update, version = self.versioning.need_update( package['output'], package['paths']) if need_update or force: output_filename = self.versioning.output_filename( package['output'], version) self.versioning.cleanup(package['output']) if self.verbose: print "Version: %s" % version print "Saving: %s" % output_filename paths = self.compile(package['paths']) content = compress( paths, asset_url=self.individual_url(output_filename), **kwargs) self.save_file(output_filename, content) else: filename_base, filename = os.path.split(package['output']) version = self.versioning.version_from_file(filename_base, filename, force=force) signal.send(sender=self, package=package, version=version, **kwargs) return self.versioning.output_filename(package['output'], version) def pack_javascripts(self, package, **kwargs): if 'externals' in package: return return self.pack(package, self.compressor.compress_js, js_compressed, templates=package['templates'], **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package['templates']) def save_file(self, path, content): return storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = {} if 'external_urls' in config[name]: packages[name]['externals'] = config[name]['external_urls'] continue paths = [] for pattern in config[name]['source_filenames']: for path in glob(pattern): if not path in paths: paths.append(str(path)) packages[name]['paths'] = [ path for path in paths if not path.endswith(settings.PIPELINE_TEMPLATE_EXT) ] packages[name]['templates'] = [ path for path in paths if path.endswith(settings.PIPELINE_TEMPLATE_EXT) ] packages[name]['output'] = config[name]['output_filename'] packages[name]['context'] = {} packages[name]['manifest'] = True if 'absolute_asset_paths' in config[name]: packages[name]['absolute_asset_paths'] = \ config[name]['absolute_asset_paths'] if 'extra_context' in config[name]: packages[name]['context'] = config[name]['extra_context'] if 'template_name' in config[name]: packages[name]['template'] = config[name]['template_name'] if 'variant' in config[name]: packages[name]['variant'] = config[name]['variant'] if 'manifest' in config[name]: packages[name]['manifest'] = config[name]['manifest'] return packages
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ _('pipeline/css/first.css'), _('pipeline/css/second.css') ], 'css/screen.css') self.assertEqual(""".concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate([ _('pipeline/js/first.js'), _('pipeline/js/second.js') ]) self.assertEqual("""function concat() {\n console.log(arguments);\n}\n\nfunction cat() {\n console.log("hello world");\n}\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path([ _('js/templates/form.jst'), _('js/templates/field.jst') ]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path('../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') def test_compile_templates(self): templates = self.compressor.compile_templates([_('pipeline/templates/photo/list.jst')]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse(self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue(self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path("../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path("/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual("""@font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); }""", output)
class Packager(object): def __init__(self, storage=None, verbose=False, css_packages=None, js_packages=None): if storage is None: storage = staticfiles_storage self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(storage=storage, verbose=verbose) if css_packages is None: css_packages = settings.STYLESHEETS if js_packages is None: js_packages = settings.JAVASCRIPT self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % ( kind, package_name ) ) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, compiler_options={}, force=False): return self.compiler.compile( paths, compiler_options=compiler_options, force=force, ) def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print("Saving: %s" % output_filename) paths = self.compile( package.paths, compiler_options=package.compiler_options, force=True, ) output_path = self.storage.path(output_filename) output_mtime = self.storage.get_modified_time(output_path) if any([self.storage.get_modified_time(self.storage.path(path)) >= output_mtime for path in paths]): content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_bytes(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
class CompressorImplementationTest(TestCase): maxDiff = None def setUp(self): self.compressor = Compressor() default_collector.collect(RequestFactory().get('/')) def tearDown(self): default_collector.clear() def _test_compressor(self, compressor_cls, compress_type, expected_file): override_settings = { ("%s_COMPRESSOR" % compress_type.upper()): compressor_cls, } with pipeline_settings(**override_settings): if compress_type == 'js': result = self.compressor.compress_js( [_('pipeline/js/first.js'), _('pipeline/js/second.js')]) else: result = self.compressor.compress_css( [_('pipeline/css/first.css'), _('pipeline/css/second.css')], os.path.join('pipeline', 'css', os.path.basename(expected_file))) with self.compressor.storage.open(expected_file) as f: expected = f.read() self.assertEqual(smart_bytes(result), expected) def test_jsmin(self): self._test_compressor('pipeline.compressors.jsmin.JSMinCompressor', 'js', 'pipeline/compressors/jsmin.js') def test_slimit(self): self._test_compressor('pipeline.compressors.slimit.SlimItCompressor', 'js', 'pipeline/compressors/slimit.js') @skipUnless(settings.HAS_NODE, "requires node") def test_uglifyjs(self): self._test_compressor('pipeline.compressors.uglifyjs.UglifyJSCompressor', 'js', 'pipeline/compressors/uglifyjs.js') @skipUnless(settings.HAS_NODE, "requires node") def test_yuglify_js(self): self._test_compressor('pipeline.compressors.yuglify.YuglifyCompressor', 'js', 'pipeline/compressors/yuglify.js') @skipUnless(settings.HAS_NODE, "requires node") def test_yuglify_css(self): self._test_compressor('pipeline.compressors.yuglify.YuglifyCompressor', 'css', 'pipeline/compressors/yuglify.css') @skipUnless(settings.HAS_NODE, "requires node") def test_cssmin(self): self._test_compressor('pipeline.compressors.cssmin.CSSMinCompressor', 'css', 'pipeline/compressors/cssmin.css') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_closure(self): self._test_compressor('pipeline.compressors.closure.ClosureCompressor', 'js', 'pipeline/compressors/closure.js') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_yui_js(self): self._test_compressor('pipeline.compressors.yui.YUICompressor', 'js', 'pipeline/compressors/yui.js') @skipUnless(settings.HAS_NODE, "requires node") @skipUnless(settings.HAS_JAVA, "requires java") def test_yui_css(self): self._test_compressor('pipeline.compressors.yui.YUICompressor', 'css', 'pipeline/compressors/yui.css') @skipUnless(settings.HAS_CSSTIDY, "requires csstidy") def test_csstidy(self): self._test_compressor('pipeline.compressors.csstidy.CSSTidyCompressor', 'css', 'pipeline/compressors/csstidy.css')
class Packager(object): def __init__(self, verbose=False, css_packages=None, js_packages=None): self.verbose = verbose self.compressor = Compressor(verbose) self.versioning = Versioning(verbose) self.compiler = Compiler(verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name].copy() except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % ( kind, package_name ) ) def individual_url(self, filename): relative_path = self.compressor.relative_path(filename)[1:] return urlparse.urljoin(settings.PIPELINE_URL, filepath_to_uri(relative_path)) def pack_stylesheets(self, package, **kwargs): variant = package.get('variant', None) absolute_asset_paths = package.get('absolute_asset_paths', True) return self.pack(package, self.compressor.compress_css, css_compressed, variant=variant, absolute_asset_paths=absolute_asset_paths, **kwargs) def compile(self, paths): return self.compiler.compile(paths) def pack(self, package, compress, signal, sync=False, force=False, **kwargs): if settings.PIPELINE_AUTO or (force and sync): need_update, version = self.versioning.need_update( package['output'], package['paths']) if need_update or force: output_filename = self.versioning.output_filename( package['output'], version ) self.versioning.cleanup(package['output']) if self.verbose: print "Version: %s" % version print "Saving: %s" % output_filename paths = self.compile(package['paths']) content = compress(paths, asset_url=self.individual_url(output_filename), **kwargs) self.save_file(output_filename, content) else: filename_base, filename = os.path.split(package['output']) version = self.versioning.version_from_file(filename_base, filename, force=force) signal.send(sender=self, package=package, version=version, **kwargs) return self.versioning.output_filename(package['output'], version) def pack_javascripts(self, package, **kwargs): if 'externals' in package: return return self.pack(package, self.compressor.compress_js, js_compressed, templates=package['templates'], **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package['templates']) def save_file(self, path, content): return storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = {} if 'external_urls' in config[name]: packages[name]['externals'] = config[name]['external_urls'] continue paths = [] for pattern in config[name]['source_filenames']: for path in glob(pattern): if not path in paths: paths.append(str(path)) packages[name]['paths'] = [path for path in paths if not path.endswith(settings.PIPELINE_TEMPLATE_EXT)] packages[name]['templates'] = [path for path in paths if path.endswith(settings.PIPELINE_TEMPLATE_EXT)] packages[name]['output'] = config[name]['output_filename'] packages[name]['context'] = {} packages[name]['manifest'] = True if 'absolute_asset_paths' in config[name]: packages[name]['absolute_asset_paths'] = \ config[name]['absolute_asset_paths'] if 'extra_context' in config[name]: packages[name]['context'] = config[name]['extra_context'] if 'template_name' in config[name]: packages[name]['template'] = config[name]['template_name'] if 'variant' in config[name]: packages[name]['variant'] = config[name]['variant'] if 'manifest' in config[name]: packages[name]['manifest'] = config[name]['manifest'] return packages
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() default_collector.collect() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite([ _('pipeline/css/first.css'), _('pipeline/css/second.css') ], 'css/screen.css') self.assertEqual(""".concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate([ _('pipeline/js/first.js'), _('pipeline/js/second.js') ]) self.assertEqual("""(function() {\n window.concat = function() {\n console.log(arguments);\n }\n}()) // No semicolon\n\n;(function() {\n window.cat = function() {\n console.log("hello world");\n }\n}());\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.asset_contents.clear() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_encoded_content_output(self): self.compressor.asset_contents.clear() encoded = self.compressor.encoded_content(_('pipeline/images/arrow.png')) expected = ('iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAYAAAARx7TFAAAAMk' 'lEQVR42oXKwQkAMAxC0Q7rEk5voSEepCHC9/SOpLV3JPULgArV' 'RtDIMEEiQ4NECRNdciCfK3K3wvEAAAAASUVORK5CYII=') self.assertEqual(encoded, expected) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path([ _('js/templates/form.jst'), _('js/templates/field.jst') ]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path( '../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path( '/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name( 'templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name( 'templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') @pipeline_settings(TEMPLATE_SEPARATOR='/') def test_template_name_separator(self): name = self.compressor.template_name( 'templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo/detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name( 'templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo/detail') def test_compile_templates(self): templates = self.compressor.compile_templates([_('pipeline/templates/photo/list.jst')]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual(templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse(self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue(self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse(self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path( "../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path( "/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual(""".embedded-url-svg { background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E% 3C/svg%3E"); } @font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); } .anchor-tag-url { background-image: url(#image-gradient); } @font-face{src:url(../pipeline/fonts/pipeline.eot);src:url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'),url(../pipeline/fonts/pipeline.woff) format('woff'),url(../pipeline/fonts/pipeline.ttf) format('truetype');} """, output) def test_url_rewrite_data_uri(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/nested/nested.css'), ], 'pipeline/screen.css') self.assertEqual(""".data-url { background-image: url(data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E); } .data-url-quoted { background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E'); } """, output) @skipIf(sys.platform.startswith("win"), "requires posix platform") def test_compressor_subprocess_unicode(self): path = os.path.dirname(os.path.dirname(__file__)) content = io.open(path + '/assets/css/unicode.css', encoding="utf-8").read() output = SubProcessCompressor(False).execute_command(('cat',), content) self.assertEqual(""".some_class { // Some unicode content: "áéíóú"; } """, output) def tearDown(self): default_collector.clear()
class Packager(object): def __init__(self, storage=default_storage, verbose=False, css_packages=None, js_packages=None): self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % ( kind, package_name ) ) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, '/*# sourceMappingURL={} */', output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, force=False): return self.compiler.compile(paths, force=force) def pack(self, package, compress, signal, source_mapping_template, **kwargs): output_filename = package.output_filename if self.verbose: print("Saving: %s" % output_filename) paths = self.compile(package.paths, force=True) content, source_map = compress(paths, **kwargs) if source_map is not None: source_map_output_filename = output_filename + '.map' self.save_file(source_map_output_filename, source_map) content = content + '\n' + source_mapping_template.format( os.path.basename(source_map_output_filename)) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, '//# sourceMappingURL={}', templates=package.templates, **kwargs) def pack_templates(self, package): return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
class Packager(object): def __init__(self, storage=default_storage, verbose=False, css_packages=None, js_packages=None): self.storage = storage self.verbose = verbose self.compressor = Compressor(storage=storage, verbose=verbose) self.compiler = Compiler(verbose=verbose) if css_packages is None: css_packages = settings.PIPELINE_CSS if js_packages is None: js_packages = settings.PIPELINE_JS self.packages = { 'css': self.create_packages(css_packages), 'js': self.create_packages(js_packages), } def package_for(self, kind, package_name): try: return self.packages[kind][package_name] except KeyError: raise PackageNotFound( "No corresponding package for %s package name : %s" % ( kind, package_name ) ) def individual_url(self, filename): return self.storage.url(filename) def pack_stylesheets(self, package, **kwargs): return self.pack(package, self.compressor.compress_css, css_compressed, output_filename=package.output_filename, variant=package.variant, **kwargs) def compile(self, paths, force=False): return self.compiler.compile(paths, force=force) def pack(self, package, compress, signal, **kwargs): output_filename = package.output_filename if self.verbose: print "Saving: %s" % output_filename paths = self.compile(package.paths, force=True) content = compress(paths, **kwargs) self.save_file(output_filename, content) signal.send(sender=self, package=package, **kwargs) return output_filename def pack_javascripts(self, package, **kwargs): return self.pack(package, self.compressor.compress_js, js_compressed, templates=package.templates, **kwargs) def pack_templates(self, package): if settings.PIPELINE_TEMPLATE_USE_CACHE and package.templates: key_partial = hashlib.md5() key_partial.update(str(package.templates)) cache_key = "%s_%s"%(key_partial.hexdigest(), translation.get_language()) default_cache = cache.get_cache('default') cached_package = default_cache.get(cache_key) if not cached_package: cached_package = self.compressor.compile_templates(package.templates) default_cache.set(cache_key, cached_package) return cached_package else: if self.verbose: print "Generating templates without using cache .." return self.compressor.compile_templates(package.templates) def save_file(self, path, content): return self.storage.save(path, ContentFile(smart_str(content))) def create_packages(self, config): packages = {} if not config: return packages for name in config: packages[name] = Package(config[name]) return packages
def setUp(self): self.compressor = Compressor() default_collector.collect(RequestFactory().get('/'))
class CompressorTest(TestCase): def setUp(self): self.maxDiff = None self.compressor = Compressor() default_collector.collect() def test_js_compressor_class(self): self.assertEqual(self.compressor.js_compressor, YuglifyCompressor) def test_css_compressor_class(self): self.assertEqual(self.compressor.css_compressor, YuglifyCompressor) def test_concatenate_and_rewrite(self): css = self.compressor.concatenate_and_rewrite( [_('pipeline/css/first.css'), _('pipeline/css/second.css')], 'css/screen.css') self.assertEqual( """.concat {\n display: none;\n}\n\n.concatenate {\n display: block;\n}\n""", css) def test_concatenate(self): js = self.compressor.concatenate( [_('pipeline/js/first.js'), _('pipeline/js/second.js')]) self.assertEqual( """(function() {\n window.concat = function() {\n console.log(arguments);\n }\n}()) // No semicolon\n\n;(function() {\n window.cat = function() {\n console.log("hello world");\n }\n}());\n""", js) @patch.object(base64, 'b64encode') def test_encoded_content(self, mock): self.compressor.asset_contents.clear() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertTrue(mock.called) mock.reset_mock() self.compressor.encoded_content(_('pipeline/images/arrow.png')) self.assertFalse(mock.called) def test_encoded_content_output(self): self.compressor.asset_contents.clear() encoded = self.compressor.encoded_content( _('pipeline/images/arrow.png')) expected = ('iVBORw0KGgoAAAANSUhEUgAAAAkAAAAGCAYAAAARx7TFAAAAMk' 'lEQVR42oXKwQkAMAxC0Q7rEk5voSEepCHC9/SOpLV3JPULgArV' 'RtDIMEEiQ4NECRNdciCfK3K3wvEAAAAASUVORK5CYII=') self.assertEqual(encoded, expected) def test_relative_path(self): relative_path = self.compressor.relative_path("images/sprite.png", 'css/screen.css') self.assertEqual(relative_path, '../images/sprite.png') def test_base_path(self): base_path = self.compressor.base_path( [_('js/templates/form.jst'), _('js/templates/field.jst')]) self.assertEqual(base_path, _('js/templates')) def test_absolute_path(self): absolute_path = self.compressor.absolute_path( '../../images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, 'images/sprite.png') absolute_path = self.compressor.absolute_path('/images/sprite.png', 'css/plugins/') self.assertEqual(absolute_path, '/images/sprite.png') def test_template_name(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo_detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo_detail') @pipeline_settings(TEMPLATE_SEPARATOR='/') def test_template_name_separator(self): name = self.compressor.template_name('templates/photo/detail.jst', 'templates/') self.assertEqual(name, 'photo/detail') name = self.compressor.template_name('templates/photo_edit.jst', '') self.assertEqual(name, 'photo_edit') name = self.compressor.template_name('templates\photo\detail.jst', 'templates\\') self.assertEqual(name, 'photo/detail') def test_compile_templates(self): templates = self.compressor.compile_templates( [_('pipeline/templates/photo/list.jst')]) self.assertEqual( templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'list\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) templates = self.compressor.compile_templates([ _('pipeline/templates/video/detail.jst'), _('pipeline/templates/photo/detail.jst') ]) self.assertEqual( templates, """window.JST = window.JST || {};\n%s\nwindow.JST[\'video_detail\'] = template(\'<div class="video">\\n <video src="<%%= src %%>" />\\n <div class="caption">\\n <%%= description %%>\\n </div>\\n</div>\');\nwindow.JST[\'photo_detail\'] = template(\'<div class="photo">\\n <img src="<%%= src %%>" />\\n <div class="caption">\\n <%%= caption %%> by <%%= author %%>\\n </div>\\n</div>\');\n""" % TEMPLATE_FUNC) def test_embeddable(self): self.assertFalse( self.compressor.embeddable(_('pipeline/images/sprite.png'), None)) self.assertFalse( self.compressor.embeddable(_('pipeline/images/arrow.png'), 'datauri')) self.assertTrue( self.compressor.embeddable(_('pipeline/images/embed/arrow.png'), 'datauri')) self.assertFalse( self.compressor.embeddable(_('pipeline/images/arrow.dat'), 'datauri')) def test_construct_asset_path(self): asset_path = self.compressor.construct_asset_path( "../../images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "../images/sprite.png") asset_path = self.compressor.construct_asset_path( "/images/sprite.png", "css/plugins/gallery.css", "css/gallery.css") self.assertEqual(asset_path, "/images/sprite.png") def test_url_rewrite(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/urls.css'), ], 'css/screen.css') self.assertEqual( """@font-face { font-family: 'Pipeline'; src: url(../pipeline/fonts/pipeline.eot); src: url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'); src: local('☺'), url(../pipeline/fonts/pipeline.woff) format('woff'), url(../pipeline/fonts/pipeline.ttf) format('truetype'), url(../pipeline/fonts/pipeline.svg#IyfZbseF) format('svg'); font-weight: normal; font-style: normal; } .relative-url { background-image: url(../pipeline/images/sprite-buttons.png); } .relative-url-querystring { background-image: url(../pipeline/images/sprite-buttons.png?v=1.0#foo=bar); } .absolute-url { background-image: url(/images/sprite-buttons.png); } .absolute-full-url { background-image: url(http://localhost/images/sprite-buttons.png); } .no-protocol-url { background-image: url(//images/sprite-buttons.png); } .anchor-tag-url { background-image: url(#image-gradient); } @font-face{src:url(../pipeline/fonts/pipeline.eot);src:url(../pipeline/fonts/pipeline.eot?#iefix) format('embedded-opentype'),url(../pipeline/fonts/pipeline.woff) format('woff'),url(../pipeline/fonts/pipeline.ttf) format('truetype');} """, output) def test_url_rewrite_data_uri(self): output = self.compressor.concatenate_and_rewrite([ _('pipeline/css/nested/nested.css'), ], 'pipeline/screen.css') self.assertEqual( """.data-url { background-image: url(data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E); } .data-url-quoted { background-image: url('data:image/svg+xml;charset=US-ASCII,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%20%20width%3D%2212px%22%20height%3D%2214px%22%20viewBox%3D%220%200%2012%2014%22%20style%3D%22enable-background%3Anew%200%200%2012%2014%3B%22%20xml%3Aspace%3D%22preserve%22%3E%3Cpath%20d%3D%22M11%2C6V5c0-2.762-2.239-5-5-5S1%2C2.238%2C1%2C5v1H0v8h12V6H11z%20M6.5%2C9.847V12h-1V9.847C5.207%2C9.673%2C5%2C9.366%2C5%2C9%20c0-0.553%2C0.448-1%2C1-1s1%2C0.447%2C1%2C1C7%2C9.366%2C6.793%2C9.673%2C6.5%2C9.847z%20M9%2C6H3V5c0-1.657%2C1.343-3%2C3-3s3%2C1.343%2C3%2C3V6z%22%2F%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3Cg%3E%3C%2Fg%3E%3C%2Fsvg%3E'); } """, output) @skipIf(sys.platform.startswith("win"), "requires posix platform") def test_compressor_subprocess_unicode(self): path = os.path.dirname(os.path.dirname(__file__)) content = io.open(path + '/assets/css/unicode.css', encoding="utf-8").read() output = SubProcessCompressor(False).execute_command(('cat', ), content) self.assertEqual( """.some_class { // Some unicode content: "áéíóú"; } """, output) def tearDown(self): default_collector.clear()
def setUp(self): self.maxDiff = None self.compressor = Compressor() default_collector.collect()