django-cotton/django_cotton/tests/utils.py
2025-03-10 18:10:23 +00:00

132 lines
4.2 KiB
Python

import os
import sys
import shutil
import tempfile
from django.urls import path
from django.conf import settings
from django.test import TestCase
from django.test import override_settings
from django.template import Context, Template
from django.views.generic import TemplateView
from django_cotton.cotton_loader import Loader as CottonLoader
class DynamicURLModule:
def __init__(self):
self.urlpatterns = []
def __call__(self):
return self.urlpatterns
class FileAlreadyExistsError(Exception):
pass
class CottonTestCase(TestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Set tmp dir and register a url module for our tmp files
cls.temp_dir = tempfile.mkdtemp()
cls.url_module = DynamicURLModule()
cls.url_module_name = f"dynamic_urls_{cls.__name__}"
sys.modules[cls.url_module_name] = cls.url_module
# Register our temp directory as a TEMPLATES path
cls.new_templates_setting = settings.TEMPLATES.copy()
cls.new_templates_setting[0]["DIRS"] = [cls.temp_dir] + cls.new_templates_setting[0]["DIRS"]
# Apply the setting
cls.templates_override = override_settings(TEMPLATES=cls.new_templates_setting)
cls.templates_override.enable()
@classmethod
def tearDownClass(cls):
"""Remove temporary directory and clean up modules"""
cls.templates_override.disable()
shutil.rmtree(cls.temp_dir, ignore_errors=True)
del sys.modules[cls.url_module_name]
super().tearDownClass()
def tearDown(self):
"""Clear state between tests so that we can use the same file names"""
self.clean_temp_dir()
super().tearDown()
def clean_temp_dir(self):
"""Remove all files in the temporary directory"""
for filename in os.listdir(self.temp_dir):
file_path = os.path.join(self.temp_dir, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception as e:
print(f"Failed to delete {file_path}. Reason: {e}")
def create_template(self, name, content, url=None, context={}):
"""Create a template file in the temporary directory and return the path"""
# To test the non-default of allowing non-snake-cased names
snake_cased_names = getattr(settings, "COTTON_SNAKE_CASED_NAMES", True)
if not snake_cased_names:
name = name.replace("_", "-")
path = os.path.join(self.temp_dir, name)
if os.path.exists(path):
raise FileAlreadyExistsError(
f"A file named '{name}' already exists in the temporary directory."
)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w") as f:
f.write(content)
if url:
# Create a dynamic class-based view
class DynamicTemplateView(TemplateView):
template_name = name
def get_context_data(self, **kwargs):
dynamic_context = super().get_context_data(**kwargs)
dynamic_context.update(context)
return dynamic_context
self.register_path(url, DynamicTemplateView.as_view(template_name=name))
return path
def make_view(self, template_name):
"""Make a view that renders the given template"""
return TemplateView.as_view(template_name=template_name)
def register_path(self, url, view):
"""Register a URL pattern and returns path"""
url_pattern = path(url, view)
self.url_module.urlpatterns.append(url_pattern)
return url_pattern
def setUp(self):
super().setUp()
self.url_module.urlpatterns = []
def url_conf(self):
return self.url_module_name
def get_compiled(template_string):
return CottonLoader(engine=None).cotton_compiler.process(template_string)
def get_rendered(template_string, context: dict = None):
if context is None:
context = {}
compiled_string = get_compiled(template_string)
return Template(compiled_string).render(Context(context))