├── .github └── workflows │ └── lint-and-test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── django_inlinecss ├── __init__.py ├── __version__.py ├── conf.py ├── css_loaders.py ├── engines.py ├── models.py ├── templatetags │ ├── __init__.py │ └── inlinecss.py └── tests │ ├── __init__.py │ ├── static │ ├── bar.css │ ├── foo.css │ └── foobar.css │ ├── templates │ ├── base.html │ ├── comments_are_ignored.html │ ├── context_vars_render_first.html │ ├── inline_css.html │ ├── multiple_staticfiles_css.html │ ├── single_staticfiles_css.html │ ├── template_inheritance.html │ ├── template_inheritance_base.html │ ├── unicode_context_variables.html │ ├── variable_and_string_defined_staticfiles_css.html │ └── variable_defined_staticfiles_css.html │ ├── test_css_loaders.py │ └── test_templatetags.py ├── setup.cfg ├── setup.py └── test_settings.py /.github/workflows/lint-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Run Tests and Linter 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - main 8 | 9 | jobs: 10 | lint-and-test: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | python-version: [3.8.18, 3.9.18, 3.10.13, 3.11.8] 16 | django-version: [3.2, 4.2] 17 | 18 | name: Lint and Test (Python ${{ matrix.python-version }} - Django ${{ matrix.django-version }}) 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Install dependencies 29 | run: | 30 | pip install -q Django==${{ matrix.django-version }} 31 | pip install -e .[flake8,tests] 32 | 33 | - name: Add current directory to PYTHONPATH 34 | run: echo "PYTHONPATH=$PWD" >> $GITHUB_ENV 35 | 36 | - name: Lint with flake8 37 | run: flake8 38 | 39 | - name: Test with pytest 40 | run: pytest 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # pyenv 78 | .python-version 79 | 80 | # celery beat schedule file 81 | celerybeat-schedule 82 | 83 | # SageMath parsed files 84 | *.sage.py 85 | 86 | # Environments 87 | .env 88 | .venv 89 | env/ 90 | venv/ 91 | ENV/ 92 | env.bak/ 93 | venv.bak/ 94 | 95 | # Spyder project settings 96 | .spyderproject 97 | .spyproject 98 | 99 | # Rope project settings 100 | .ropeproject 101 | 102 | # mkdocs documentation 103 | /site 104 | 105 | # mypy 106 | .mypy_cache/ 107 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.5.0 4 | hooks: 5 | - id: trailing-whitespace 6 | args: [--markdown-linebreak-ext=md] 7 | - id: end-of-file-fixer 8 | - id: check-toml 9 | - id: check-added-large-files 10 | - id: debug-statements 11 | - repo: https://github.com/PyCQA/flake8 12 | rev: "7.0.0" 13 | hooks: 14 | - id: flake8 15 | additional_dependencies: 16 | - flake8-bugbear 17 | - flake8-isort 18 | - repo: https://github.com/psf/black 19 | rev: "24.2.0" 20 | hooks: 21 | - id: black 22 | - repo: https://github.com/pycqa/isort 23 | rev: 5.13.2 24 | hooks: 25 | - id: isort 26 | name: isort (python) 27 | - repo: https://github.com/myint/autoflake 28 | rev: v2.2.1 29 | hooks: 30 | - id: autoflake 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## django-inlinecss 2 | 3 | Copyright (c) 2012 Philip Kimmey & Thomas Yip 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | ## pynliner 10 | 11 | Pynliner is included along with additional improvements to soupselect that are not 12 | broadly available (via PyPI for example). 13 | 14 | Copyright (c) 2011 Tanner Netterville 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 19 | 20 | The generated output of this software shall not be used in a mass marketing service. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | recursive-include django_inlinecss *.py 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/roverdotcom/django-inlinecss.svg?branch=master)](https://travis-ci.org/roverdotcom/django-inlinecss) 2 | 3 | ## About 4 | 5 | Inlining CSS is necessary for email generation and sending 6 | but is currently a surprisingly large hassle. 7 | 8 | This library aims to make it a breeze in the Django 9 | template language. 10 | 11 | ## Usage 12 | 13 | #### Step 1: Dependencies 14 | 15 | - BeautifulSoup 16 | - cssutils 17 | - Python 3.8+ 18 | - Django 3.2+ 19 | 20 | 21 | #### Step 2: Install django_inlinecss 22 | 23 | Add ```django_inlinecss``` to your ```settings.py```: 24 | 25 | ```python 26 | INSTALLED_APPS = ( 27 | 'django.contrib.auth', 28 | 'django.contrib.webdesign', 29 | 'django.contrib.contenttypes', 30 | '...', 31 | '...', 32 | '...', 33 | 'django_inlinecss') 34 | ``` 35 | 36 | #### Step 3: Use the templatetag 37 | 38 | 1. Place your CSS file somewhere staticfiles can find it 39 | 2. Create your template: 40 | 41 | ```html 42 | {% load inlinecss %} 43 | {% inlinecss "css/extra-padding.css" %} 44 | 45 | 46 |
47 | Something in need of styling. 48 |
49 | 50 | 51 | {% endinlinecss %} 52 | ``` 53 | 54 | #### Step 4: Prepare to be Wowed 55 | 56 | ```html 57 | 58 | 59 |
60 | Something in need of styling. 61 |
62 | 63 | 64 | ``` 65 | 66 | ## Acknowledgements 67 | 68 | Thanks to Tanner Netterville for his efforts on [Pynliner](https://github.com/rennat/pynliner). 69 | 70 | Thanks to Thomas Yip for his unit tests on the `soupselect` module. These tests 71 | helped on getting the core CSS2 selectors to work. 72 | 73 | ## License 74 | 75 | MIT license. See LICENSE.md for more detail. 76 | -------------------------------------------------------------------------------- /django_inlinecss/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roverdotcom/django-inlinecss/d69f93bbc241f372e5a27342d42948285cf1d97b/django_inlinecss/__init__.py -------------------------------------------------------------------------------- /django_inlinecss/__version__.py: -------------------------------------------------------------------------------- 1 | VERSION = (0, 4, 0) 2 | 3 | __version__ = ".".join(map(str, VERSION)) 4 | -------------------------------------------------------------------------------- /django_inlinecss/conf.py: -------------------------------------------------------------------------------- 1 | try: 2 | import importlib 3 | except ImportError: 4 | from django.utils import importlib 5 | 6 | DEFAULT_ENGINE = "django_inlinecss.engines.PynlinerEngine" 7 | DEFAULT_CSS_LOADER = "django_inlinecss.css_loaders.StaticfilesStorageCSSLoader" 8 | 9 | 10 | def load_class_by_path(path): 11 | i = path.rfind(".") 12 | module_path, class_name = path[:i], path[i + 1 :] 13 | module = importlib.import_module(module_path) 14 | return getattr(module, class_name) 15 | 16 | 17 | def get_engine(): 18 | from django.conf import settings 19 | 20 | engine_path = getattr(settings, "INLINECSS_ENGINE", DEFAULT_ENGINE) 21 | return load_class_by_path(engine_path) 22 | 23 | 24 | def get_css_loader(): 25 | from django.conf import settings 26 | 27 | engine_path = getattr(settings, "INLINECSS_CSS_LOADER", DEFAULT_CSS_LOADER) 28 | return load_class_by_path(engine_path) 29 | -------------------------------------------------------------------------------- /django_inlinecss/css_loaders.py: -------------------------------------------------------------------------------- 1 | from django.contrib.staticfiles import finders 2 | from django.contrib.staticfiles.storage import staticfiles_storage 3 | 4 | 5 | class BaseCSSLoader: 6 | def __init__(self): 7 | pass 8 | 9 | def load(self, path): 10 | """ 11 | Retrieves the contents of the static asset specified 12 | :param path: path to the desired asset 13 | :return: contents of asset 14 | """ 15 | raise NotImplementedError() 16 | 17 | 18 | class StaticfilesFinderCSSLoader(BaseCSSLoader): 19 | def load(self, path): 20 | """ 21 | Retrieve CSS contents from the local filesystem with static finders 22 | """ 23 | expanded_path = finders.find(path) 24 | 25 | if expanded_path is None: 26 | raise OSError(f"{path} does not exist") 27 | 28 | with open(expanded_path, "rb") as css_file: 29 | return css_file.read().decode("utf-8") 30 | 31 | 32 | class StaticfilesStorageCSSLoader(BaseCSSLoader): 33 | def load(self, path): 34 | """ 35 | Retrieve CSS contents with staticfiles storage 36 | """ 37 | return staticfiles_storage.open(path).read().decode("utf-8") 38 | -------------------------------------------------------------------------------- /django_inlinecss/engines.py: -------------------------------------------------------------------------------- 1 | import pynliner 2 | 3 | 4 | class EngineBase: 5 | def __init__(self, html, css): 6 | self.html = html 7 | self.css = css 8 | 9 | def render(self): 10 | raise NotImplementedError() 11 | 12 | 13 | class PynlinerEngine(EngineBase): 14 | def render(self): 15 | inliner = pynliner.Pynliner().from_string(self.html) 16 | inliner = inliner.with_cssString(self.css) 17 | return inliner.run() 18 | 19 | 20 | class NullEngine(EngineBase): 21 | def render(self): 22 | return self.html 23 | -------------------------------------------------------------------------------- /django_inlinecss/models.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roverdotcom/django-inlinecss/d69f93bbc241f372e5a27342d42948285cf1d97b/django_inlinecss/models.py -------------------------------------------------------------------------------- /django_inlinecss/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roverdotcom/django-inlinecss/d69f93bbc241f372e5a27342d42948285cf1d97b/django_inlinecss/templatetags/__init__.py -------------------------------------------------------------------------------- /django_inlinecss/templatetags/inlinecss.py: -------------------------------------------------------------------------------- 1 | from django import template 2 | from django.utils.encoding import smart_str 3 | 4 | from django_inlinecss import conf 5 | 6 | register = template.Library() 7 | 8 | 9 | class InlineCssNode(template.Node): 10 | def __init__(self, nodelist, filter_expressions): 11 | self.nodelist = nodelist 12 | self.filter_expressions = filter_expressions 13 | 14 | def render(self, context): 15 | rendered_contents = self.nodelist.render(context) 16 | css = "" 17 | for expression in self.filter_expressions: 18 | path = expression.resolve(context, True) 19 | if path is not None: 20 | path = smart_str(path) 21 | 22 | css_loader = conf.get_css_loader()() 23 | css = "".join((css, css_loader.load(path))) 24 | 25 | engine = conf.get_engine()(html=rendered_contents, css=css) 26 | return engine.render() 27 | 28 | 29 | @register.tag 30 | def inlinecss(parser, token): 31 | nodelist = parser.parse(("endinlinecss",)) 32 | 33 | # prevent second parsing of endinlinecss 34 | parser.delete_first_token() 35 | 36 | args = token.split_contents()[1:] 37 | 38 | return InlineCssNode(nodelist, [parser.compile_filter(arg) for arg in args]) 39 | -------------------------------------------------------------------------------- /django_inlinecss/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roverdotcom/django-inlinecss/d69f93bbc241f372e5a27342d42948285cf1d97b/django_inlinecss/tests/__init__.py -------------------------------------------------------------------------------- /django_inlinecss/tests/static/bar.css: -------------------------------------------------------------------------------- 1 | div.bar { 2 | padding: 10px 15px 20px 25px; 3 | } 4 | -------------------------------------------------------------------------------- /django_inlinecss/tests/static/foo.css: -------------------------------------------------------------------------------- 1 | div.foo { 2 | margin: 10px 15px 20px 25px; 3 | } 4 | -------------------------------------------------------------------------------- /django_inlinecss/tests/static/foobar.css: -------------------------------------------------------------------------------- 1 | div.foo { 2 | margin: 10px 15px 20px 25px; 3 | } 4 | div.bar { 5 | padding: 10px 15px 20px 25px; 6 | } 7 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/base.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roverdotcom/django-inlinecss/d69f93bbc241f372e5a27342d42948285cf1d97b/django_inlinecss/tests/templates/base.html -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/comments_are_ignored.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | 3 | {% inlinecss "foobar.css" %} 4 | 5 | 6 |
7 | This is the "foo" div. 8 | 9 |
10 |
11 | This is the "bar" div. 12 | 13 |
14 | 15 | {% endinlinecss %} 16 | 17 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/context_vars_render_first.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss "foobar.css" %} 3 | 4 | 5 | {{ foo_div_open_tag }} 6 | This is the "foo" div. 7 | 8 | {{ bar_div_open_tag }} 9 | This is the "bar" div. 10 | 11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/inline_css.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss %} 3 | 4 | 5 | 13 | 14 | 15 |
16 | This is the "foo" div. 17 |
18 |
19 | This is the "bar" div. 20 |
21 | 22 | 23 | {% endinlinecss %} 24 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/multiple_staticfiles_css.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss "foo.css" "bar.css" %} 3 | 4 | 5 |
6 | This is the "foo" div. 7 |
8 |
9 | This is the "bar" div. 10 |
11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/single_staticfiles_css.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss "foobar.css" %} 3 | 4 | 5 |
6 | This is the "foo" div. 7 |
8 |
9 | This is the "bar" div. 10 |
11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/template_inheritance.html: -------------------------------------------------------------------------------- 1 | {% extends "template_inheritance_base.html" %} 2 | 3 | {% block content %} 4 |
5 | This is the "foo" div. 6 |
7 |
8 | This is the "bar" div. 9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/template_inheritance_base.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss "foobar.css" %} 3 | 4 | 5 | {% block content %} 6 | 7 | {% endblock %} 8 | 9 | 10 | {% endinlinecss %} 11 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/unicode_context_variables.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss "foobar.css" %} 3 | 4 | 5 |
6 | This is the "foo" div. 7 |
8 |
9 | {{ unicode_string }} 10 |
11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/variable_and_string_defined_staticfiles_css.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss foo_css "bar.css" %} 3 | 4 | 5 |
6 | This is the "foo" div. 7 |
8 |
9 | This is the "bar" div. 10 |
11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/templates/variable_defined_staticfiles_css.html: -------------------------------------------------------------------------------- 1 | {% load inlinecss %} 2 | {% inlinecss foo_css bar_css %} 3 | 4 | 5 |
6 | This is the "foo" div. 7 |
8 |
9 | This is the "bar" div. 10 |
11 | 12 | 13 | {% endinlinecss %} 14 | -------------------------------------------------------------------------------- /django_inlinecss/tests/test_css_loaders.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test CSS loaders 3 | """ 4 | 5 | from django.conf import settings 6 | from django.test import TestCase 7 | from django.test import override_settings 8 | 9 | from django_inlinecss.css_loaders import StaticfilesFinderCSSLoader 10 | from django_inlinecss.css_loaders import StaticfilesStorageCSSLoader 11 | 12 | 13 | @override_settings(STATICFILES_DIRS=[settings.STATIC_ROOT], STATIC_ROOT="") 14 | class StaticfilesFinderCSSLoaderTestCase(TestCase): 15 | def setUp(self): 16 | self.loader = StaticfilesFinderCSSLoader() 17 | super().setUp() 18 | 19 | def test_loads_existing_css_file(self): 20 | css = self.loader.load("bar.css") 21 | self.assertIn("div.bar {", css) 22 | 23 | def test_load_file_does_not_exist(self): 24 | with self.assertRaises(IOError) as e: 25 | self.loader.load("missing.css") 26 | 27 | self.assertEqual(str(e.exception), "missing.css does not exist") 28 | 29 | 30 | class StaticfilesStorageCSSLoaderTestCase(TestCase): 31 | def setUp(self): 32 | self.loader = StaticfilesStorageCSSLoader() 33 | super().setUp() 34 | 35 | def test_loads_existing_css_file(self): 36 | css = self.loader.load("bar.css") 37 | self.assertIn("div.bar {", css) 38 | 39 | def test_load_file_does_not_exist(self): 40 | with self.assertRaises(IOError) as e: 41 | self.loader.load("missing.css") 42 | 43 | self.assertEqual(e.exception.strerror, "No such file or directory") 44 | -------------------------------------------------------------------------------- /django_inlinecss/tests/test_templatetags.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test the functioning of the templatetag itself. 3 | 4 | The actual CSS inlining displayed here is extremely simple: 5 | tests of the CSS selector functionality is independent. 6 | """ 7 | 8 | import os 9 | from unittest.mock import patch 10 | 11 | from django.conf import settings 12 | from django.template.loader import get_template 13 | from django.test import TestCase 14 | from django.test.utils import override_settings 15 | from django.utils.safestring import mark_safe 16 | 17 | 18 | class InlinecssTests(TestCase): 19 | def setUp(self): 20 | super().setUp() 21 | 22 | def assert_foo_and_bar_rendered(self, rendered): 23 | foo_div_regex = ( 24 | r'
' 25 | r'\s+This is the "foo" div.\s+' 26 | r"<\/div>" 27 | ) 28 | self.assertRegex(rendered, foo_div_regex) 29 | 30 | bar_div_regex = ( 31 | r'
' 32 | r'\s+This is the "bar" div.\s+' 33 | r"<\/div>" 34 | ) 35 | self.assertRegex(rendered, bar_div_regex) 36 | 37 | def test_single_staticfiles_css(self): 38 | """ 39 | Test the basic inlining case of using the staticfiles loader 40 | to load a CSS file and inline it as part of a rendering step. 41 | """ 42 | template = get_template("single_staticfiles_css.html") 43 | rendered = template.render({}) 44 | self.assert_foo_and_bar_rendered(rendered) 45 | 46 | def test_multiple_staticfiles_css(self): 47 | """ 48 | Test the multiple inlining case of using the staticfiles loader. 49 | 50 | This tests that passing two css files works. 51 | """ 52 | template = get_template("multiple_staticfiles_css.html") 53 | rendered = template.render({}) 54 | self.assert_foo_and_bar_rendered(rendered) 55 | 56 | def test_variable_defined_staticfiles_css(self): 57 | """ 58 | Test that the staticfiles paths passed to the templatetag 59 | may be defined as variables instead of strings. 60 | """ 61 | template = get_template("variable_defined_staticfiles_css.html") 62 | context = {"foo_css": "foo.css", "bar_css": "bar.css"} 63 | 64 | rendered = template.render(context) 65 | self.assert_foo_and_bar_rendered(rendered) 66 | 67 | def test_variable_and_string_defined_staticfiles_css(self): 68 | """ 69 | Test that we can mix and match variable-defined CSS files & 70 | those defined quoted in the templatetag. 71 | """ 72 | template = get_template("variable_and_string_defined_staticfiles_css.html") 73 | context = {"foo_css": "foo.css"} 74 | rendered = template.render(context) 75 | self.assert_foo_and_bar_rendered(rendered) 76 | 77 | def test_inline_css(self): 78 | """ 79 | Test that