refactor: Add Node metadata (#1229)

* refactor: `Slot.source` replaced with `Slot.fill_node`, new `Component.node` property, and `slot_node` available in `on_slot_rendered()` hook.

* refactor: fix windows path error in tests
This commit is contained in:
Juro Oravec 2025-06-03 12:58:48 +02:00 committed by GitHub
parent abc6be343e
commit 46e524e37d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 728 additions and 103 deletions

View file

@ -1,12 +1,15 @@
import inspect
import os
import re
from typing import cast
import pytest
from django.template import Context, Template
from django.template.base import TextNode, VariableNode
from django.template.defaulttags import IfNode, LoremNode
from django.template.exceptions import TemplateSyntaxError
from django_components import types
from django_components import Component, types
from django_components.node import BaseNode, template_tag
from django_components.templatetags import component_tags
from django_components.util.tag_parser import TagAttr
@ -849,7 +852,14 @@ class TestSignatureBasedValidation:
@force_signature_validation
def render(self, context: Context, name: str, **kwargs) -> str:
nonlocal captured
captured = self.params, self.nodelist, self.node_id, self.contents
captured = (
self.params,
self.nodelist,
self.node_id,
self.contents,
self.template_name,
self.template_component,
)
return f"Hello, {name}!"
# Case 1 - Node with end tag and NOT self-closing
@ -864,7 +874,7 @@ class TestSignatureBasedValidation:
template1 = Template(template_str1)
template1.render(Context({}))
params1, nodelist1, node_id1, contents1 = captured # type: ignore
params1, nodelist1, node_id1, contents1, template_name1, template_component1 = captured # type: ignore
assert len(params1) == 1
assert isinstance(params1[0], TagAttr)
# NOTE: The comment node is not included in the nodelist
@ -879,6 +889,8 @@ class TestSignatureBasedValidation:
assert isinstance(nodelist1[7], TextNode)
assert contents1 == "\n INSIDE TAG {{ my_var }} {# comment #} {% lorem 1 w %} {% if True %} henlo {% endif %}\n " # noqa: E501
assert node_id1 == "a1bc3e"
assert template_name1 == '<unknown source>'
assert template_component1 is None
captured = None # Reset captured
@ -890,12 +902,14 @@ class TestSignatureBasedValidation:
template2 = Template(template_str2)
template2.render(Context({}))
params2, nodelist2, node_id2, contents2 = captured # type: ignore
params2, nodelist2, node_id2, contents2, template_name2, template_component2 = captured # type: ignore
assert len(params2) == 1 # type: ignore
assert isinstance(params2[0], TagAttr) # type: ignore
assert len(nodelist2) == 0 # type: ignore
assert contents2 is None # type: ignore
assert node_id2 == "a1bc3f" # type: ignore
assert template_name2 == '<unknown source>' # type: ignore
assert template_component2 is None # type: ignore
captured = None # Reset captured
@ -906,7 +920,7 @@ class TestSignatureBasedValidation:
@force_signature_validation
def render(self, context: Context, name: str, **kwargs) -> str:
nonlocal captured
captured = self.params, self.nodelist, self.node_id, self.contents
captured = self.params, self.nodelist, self.node_id, self.contents, self.template_name, self.template_component # noqa: E501
return f"Hello, {name}!"
TestNodeWithoutEndTag.register(component_tags.register)
@ -918,12 +932,38 @@ class TestSignatureBasedValidation:
template3 = Template(template_str3)
template3.render(Context({}))
params3, nodelist3, node_id3, contents3 = captured # type: ignore
params3, nodelist3, node_id3, contents3, template_name3, template_component3 = captured # type: ignore
assert len(params3) == 1 # type: ignore
assert isinstance(params3[0], TagAttr) # type: ignore
assert len(nodelist3) == 0 # type: ignore
assert contents3 is None # type: ignore
assert node_id3 == "a1bc40" # type: ignore
assert template_name3 == '<unknown source>' # type: ignore
assert template_component3 is None # type: ignore
# Case 4 - Node nested in Component end tag
class TestComponent(Component):
template = """
{% load component_tags %}
{% mytag2 'John' %}
"""
TestComponent.render(Context({}))
params4, nodelist4, node_id4, contents4, template_name4, template_component4 = captured # type: ignore
assert len(params4) == 1 # type: ignore
assert isinstance(params4[0], TagAttr) # type: ignore
assert len(nodelist4) == 0 # type: ignore
assert contents4 is None # type: ignore
assert node_id4 == "a1bc42" # type: ignore
if os.name == "nt":
assert cast(str, template_name4).endswith("\\tests\\test_node.py::TestComponent") # type: ignore
else:
assert cast(str, template_name4).endswith("/tests/test_node.py::TestComponent") # type: ignore
assert template_name4 == f"{__file__}::TestComponent" # type: ignore
assert template_component4 is TestComponent # type: ignore
# Cleanup
TestNodeWithEndTag.unregister(component_tags.register)