django-components/tests/test_component_as_view.py
Juro Oravec 5fd45ab424
chore: Push dev to master to release v0.110 (#767)
* 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>
2024-11-25 09:41:57 +01:00

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,
)