Skip to content

Commit

Permalink
Make it possible to trace evaluation in tests via a trace fixture.
Browse files Browse the repository at this point in the history
  • Loading branch information
pelme committed Sep 15, 2024
1 parent 1afddc2 commit f27fc9c
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 43 deletions.
Empty file added tests/__init__.py
Empty file.
38 changes: 33 additions & 5 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from __future__ import annotations

import dataclasses
import typing as t

import pytest

from htpy import Node, iter_node

if t.TYPE_CHECKING:
from collections.abc import Generator
from collections.abc import Callable, Generator


RenderFixture: t.TypeAlias = t.Callable[[Node], list[str]]
@dataclasses.dataclass(frozen=True)
class Trace:
description: str


RenderResult: t.TypeAlias = list[str | Trace]
RenderFixture: t.TypeAlias = t.Callable[[Node], RenderResult]
TraceFixture: t.TypeAlias = t.Callable[[str], None]


@pytest.fixture(scope="session")
Expand All @@ -28,13 +36,33 @@ def django_env() -> None:


@pytest.fixture
def render() -> Generator[RenderFixture, None, None]:
def render_result() -> RenderResult:
return []


@pytest.fixture
def trace(render_result: RenderResult) -> Callable[[str], None]:
def func(description: str) -> None:
render_result.append(Trace(description=description))

return func


@pytest.fixture
def render(render_result: RenderResult) -> Generator[RenderFixture, None, None]:
called = False

def func(node: Node) -> list[str]:
def func(node: Node) -> RenderResult:
nonlocal called

if called:
raise AssertionError("render() must only be called once per test")

called = True
return list(iter_node(node))
for chunk in iter_node(node):
render_result.append(chunk)

return render_result

yield func

Expand Down
10 changes: 6 additions & 4 deletions tests/test_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,16 @@ def test_attrs_and_kwargs(render: RenderFixture) -> None:
assert render(result) == ['<div a="1" for="b" b="2">', "</div>"]


def test_class_priority(render: RenderFixture) -> None:
result = div(".a", {"class": "b"}, class_="c")
assert render(result) == ['<div class="c">', "</div>"]

def test_class_priority_dict(render: RenderFixture) -> None:
result = div(".a", {"class": "b"})
assert render(result) == ['<div class="b">', "</div>"]


def test_class_priority_dict_and_kwarg(render: RenderFixture) -> None:
result = div(".a", {"class": "b"}, class_="c")
assert render(result) == ['<div class="c">', "</div>"]


def test_attribute_priority(render: RenderFixture) -> None:
result = div({"foo": "a"}, foo="b")
assert render(result) == ['<div foo="b">', "</div>"]
Expand Down
67 changes: 33 additions & 34 deletions tests/test_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@

from htpy import Element, VoidElement, dd, div, dl, dt, html, img, input, li, my_custom_element, ul

from .conftest import Trace

if t.TYPE_CHECKING:
from collections.abc import Callable, Generator

from htpy import Node

from .conftest import RenderFixture
from .conftest import RenderFixture, TraceFixture


def test_void_element(render: RenderFixture) -> None:
Expand Down Expand Up @@ -134,29 +136,43 @@ def test_ignored(render: RenderFixture, ignored_value: t.Any) -> None:
assert render(div[ignored_value]) == ["<div>", "</div>"]


def test_sync_iter() -> None:
trace = "not started"

def test_lazy_iter(render: RenderFixture, trace: TraceFixture) -> None:
def generate_list() -> Generator[Element, None, None]:
nonlocal trace

trace = "before yield"
trace("before yield")
yield li("#a")
trace("after yield")

result = render(ul[generate_list()])
assert result == [
"<ul>",
Trace("before yield"),
'<li id="a">',
"</li>",
Trace("after yield"),
"</ul>",
]

trace = "done"

iterator = iter(ul[generate_list()])
def test_lazy_callable(render: RenderFixture, trace: TraceFixture) -> None:
def parent() -> Element:
trace("in parent")
return div("#parent")[child]

assert next(iterator) == "<ul>"
assert trace == "not started"
def child() -> str:
trace("in child")
return "child"

assert next(iterator) == '<li id="a">'
assert trace == "before yield"
assert next(iterator) == "</li>"
assert trace == "before yield"
result = render(div[parent])

assert next(iterator) == "</ul>"
assert trace == "done"
assert result == [
"<div>",
Trace(description="in parent"),
'<div id="parent">',
Trace(description="in child"),
"child",
"</div>",
"</div>",
]


def test_iter_str(render: RenderFixture) -> None:
Expand All @@ -175,23 +191,6 @@ def test_iter_markup(render: RenderFixture) -> None:
assert type(child) is str


def test_callable() -> None:
called = False

def generate_img() -> VoidElement:
nonlocal called
called = True
return img

iterator = iter(div[generate_img])

assert next(iterator) == "<div>"
assert called is False
assert next(iterator) == "<img>"
assert called is True
assert next(iterator) == "</div>"


def test_escape_children(render: RenderFixture) -> None:
result = render(div['>"'])
assert result == ["<div>", "&gt;&#34;", "</div>"]
Expand Down

0 comments on commit f27fc9c

Please sign in to comment.