mirror of
https://github.com/django-components/django-components.git
synced 2025-09-03 20:50:31 +00:00

* feat: skeleton of dependency manager backend (#688) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: selectolax update and tests cleanup (#702) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: move release notes to own file (#704) * chore: merge changes from master (#705) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Yassin Rakha <yaso2go@gmail.com> Co-authored-by: Emil Stenström <emil@emilstenstrom.se> fix for nested slots (#698) (#699) * refactor: remove joint {% component_dependencies %} tag (#706) Co-authored-by: Emil Stenström <emil@emilstenstrom.se> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: split up utils file and move utils to util dir (#707) * docs: Move docs inside src/ to allow imports in python scripts (#708) * refactor: Docs prep 1 (#715) * refactor: Document template tags (#716) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: pass slot fills in template via slots param (#719) * chore: Merge master to dev (#729) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Yassin Rakha <yaso2go@gmail.com> Co-authored-by: Emil Stenström <emil@emilstenstrom.se> Co-authored-by: Tom Larsen <larsent@gmail.com> fix for nested slots (#698) (#699) * fix: Do not raise error if multiple slots with same name are flagged as default (#727) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: tag formatter - allow fwd slash in end tag (#730) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * refactor: Use lowercase names for registry settings (#731) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * docs: add docstrings (#732) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * feat: define settings as a data class for type hints, intellisense, and docs (#733) * refactor: fix reload-on-change logic, expose autodiscover's dirs-getting logic, rename settings (#734) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * docs: document settings (#743) * docs: document settings * refactor: fix linter errors * feat: passthrough slots and more (#758) * feat: passthrough slots and more * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * refactor: remove ComponentSlotContext.slots * refactor: update comment * docs: update changelog * refactor: update docstrings * refactor: document and test-cover more changes * refactor: revert fill without name * docs: update README --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * fix: apostrophes in tags (#765) * refactor: fix merge error - duplicate code --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Emil Stenström <emil@emilstenstrom.se>
277 lines
10 KiB
Python
277 lines
10 KiB
Python
from typing import Any, Dict
|
|
|
|
from django.conf import settings
|
|
from django.http import HttpResponse
|
|
from django.template import Context, Template
|
|
from django.test import Client
|
|
from django.urls import path
|
|
|
|
from django_components import Component, ComponentView, register, types
|
|
from django_components.urls import urlpatterns as dc_urlpatterns
|
|
|
|
from .django_test_setup import setup_test_config
|
|
from .testutils import BaseTestCase, parametrize_context_behavior
|
|
|
|
setup_test_config({"autodiscover": False})
|
|
|
|
|
|
class CustomClient(Client):
|
|
def __init__(self, urlpatterns=None, *args, **kwargs):
|
|
import types
|
|
|
|
if urlpatterns:
|
|
urls_module = types.ModuleType("urls")
|
|
urls_module.urlpatterns = urlpatterns + dc_urlpatterns # type: ignore
|
|
settings.ROOT_URLCONF = urls_module
|
|
else:
|
|
settings.ROOT_URLCONF = __name__
|
|
settings.SECRET_KEY = "secret" # noqa
|
|
super().__init__(*args, **kwargs)
|
|
|
|
|
|
class TestComponentAsView(BaseTestCase):
|
|
def test_render_component_from_template(self):
|
|
@register("testcomponent")
|
|
class MockComponentRequest(Component):
|
|
template = """
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="text" name="variable" value="{{ variable }}">
|
|
<input type="submit">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable, *args, **kwargs) -> Dict[str, Any]:
|
|
return {"variable": variable}
|
|
|
|
def render_template_view(request):
|
|
template = Template(
|
|
"""
|
|
{% load component_tags %}
|
|
{% component "testcomponent" variable="TEMPLATE" %}{% endcomponent %}
|
|
"""
|
|
)
|
|
return HttpResponse(template.render(Context({})))
|
|
|
|
client = CustomClient(urlpatterns=[path("test_template/", render_template_view)])
|
|
response = client.get("/test_template/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="TEMPLATE">',
|
|
response.content,
|
|
)
|
|
|
|
def test_get_request(self):
|
|
class MockComponentRequest(Component):
|
|
template = """
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="text" name="variable" value="{{ inner_var }}">
|
|
<input type="submit">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable):
|
|
return {"inner_var": variable}
|
|
|
|
class View(ComponentView):
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.component.render_to_response(kwargs={"variable": "GET"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test/", MockComponentRequest.as_view())])
|
|
response = client.get("/test/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="GET">',
|
|
response.content,
|
|
)
|
|
|
|
def test_get_request_shortcut(self):
|
|
class MockComponentRequest(Component):
|
|
template = """
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="text" name="variable" value="{{ inner_var }}">
|
|
<input type="submit">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable):
|
|
return {"inner_var": variable}
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response(kwargs={"variable": "GET"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test/", MockComponentRequest.as_view())])
|
|
response = client.get("/test/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="GET">',
|
|
response.content,
|
|
)
|
|
|
|
def test_post_request(self):
|
|
class MockComponentRequest(Component):
|
|
template: types.django_html = """
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="text" name="variable" value="{{ inner_var }}">
|
|
<input type="submit">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable):
|
|
return {"inner_var": variable}
|
|
|
|
class View(ComponentView):
|
|
def post(self, request, *args, **kwargs) -> HttpResponse:
|
|
variable = request.POST.get("variable")
|
|
return self.component.render_to_response(kwargs={"variable": variable})
|
|
|
|
client = CustomClient(urlpatterns=[path("test/", MockComponentRequest.as_view())])
|
|
response = client.post("/test/", {"variable": "POST"})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="POST">',
|
|
response.content,
|
|
)
|
|
|
|
def test_post_request_shortcut(self):
|
|
class MockComponentRequest(Component):
|
|
template: types.django_html = """
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<input type="text" name="variable" value="{{ inner_var }}">
|
|
<input type="submit">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable):
|
|
return {"inner_var": variable}
|
|
|
|
def post(self, request, *args, **kwargs) -> HttpResponse:
|
|
variable = request.POST.get("variable")
|
|
return self.render_to_response(kwargs={"variable": variable})
|
|
|
|
client = CustomClient(urlpatterns=[path("test/", MockComponentRequest.as_view())])
|
|
response = client.post("/test/", {"variable": "POST"})
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="POST">',
|
|
response.content,
|
|
)
|
|
|
|
def test_instantiate_component(self):
|
|
class MockComponentRequest(Component):
|
|
template = """
|
|
<form method="post">
|
|
<input type="text" name="variable" value="{{ inner_var }}">
|
|
</form>
|
|
"""
|
|
|
|
def get_context_data(self, variable):
|
|
return {"inner_var": variable}
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response(kwargs={"variable": self.name})
|
|
|
|
client = CustomClient(urlpatterns=[path("test/", MockComponentRequest("my_comp").as_view())])
|
|
response = client.get("/test/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b'<input type="text" name="variable" value="my_comp">',
|
|
response.content,
|
|
)
|
|
|
|
@parametrize_context_behavior(["django", "isolated"])
|
|
def test_replace_slot_in_view(self):
|
|
class MockComponentSlot(Component):
|
|
template = """
|
|
{% load component_tags %}
|
|
<div>
|
|
{% slot "first_slot" %}
|
|
Hey, I'm {{ name }}
|
|
{% endslot %}
|
|
{% slot "second_slot" %}
|
|
{% endslot %}
|
|
</div>
|
|
"""
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response({"name": "Bob"}, {"second_slot": "Nice to meet you, Bob"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test_slot/", MockComponentSlot.as_view())])
|
|
response = client.get("/test_slot/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b"Hey, I'm Bob",
|
|
response.content,
|
|
)
|
|
self.assertIn(
|
|
b"Nice to meet you, Bob",
|
|
response.content,
|
|
)
|
|
|
|
@parametrize_context_behavior(["django", "isolated"])
|
|
def test_replace_slot_in_view_with_insecure_content(self):
|
|
class MockInsecureComponentSlot(Component):
|
|
template = """
|
|
{% load component_tags %}
|
|
<div>
|
|
{% slot "test_slot" %}
|
|
{% endslot %}
|
|
</div>
|
|
"""
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response({}, {"test_slot": "<script>alert(1);</script>"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test_slot_insecure/", MockInsecureComponentSlot.as_view())])
|
|
response = client.get("/test_slot_insecure/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertNotIn(
|
|
b"<script>",
|
|
response.content,
|
|
)
|
|
|
|
@parametrize_context_behavior(["django", "isolated"])
|
|
def test_replace_context_in_view(self):
|
|
class TestComponent(Component):
|
|
template = """
|
|
{% load component_tags %}
|
|
<div>
|
|
Hey, I'm {{ name }}
|
|
</div>
|
|
"""
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response({"name": "Bob"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test_context_django/", TestComponent.as_view())])
|
|
response = client.get("/test_context_django/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertIn(
|
|
b"Hey, I'm Bob",
|
|
response.content,
|
|
)
|
|
|
|
@parametrize_context_behavior(["django", "isolated"])
|
|
def test_replace_context_in_view_with_insecure_content(self):
|
|
class MockInsecureComponentContext(Component):
|
|
template = """
|
|
{% load component_tags %}
|
|
<div>
|
|
{{ variable }}
|
|
</div>
|
|
"""
|
|
|
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
|
return self.render_to_response({"variable": "<script>alert(1);</script>"})
|
|
|
|
client = CustomClient(urlpatterns=[path("test_context_insecure/", MockInsecureComponentContext.as_view())])
|
|
response = client.get("/test_context_insecure/")
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertNotIn(
|
|
b"<script>",
|
|
response.content,
|
|
)
|