from textwrap import dedent import pytest from pytest import raises as assert_raises from minihtml import CircularReferenceError, make_prototype, safe, text div = make_prototype("div") span = make_prototype("span", inline=True) img = make_prototype("img", inline=True, empty=True, omit_end_tag=True) iframe = make_prototype("iframe", empty=True, omit_end_tag=False) def test_prototype_repr(): assert repr(div) == "" assert repr(span) == "" assert repr(img) == "" assert repr(iframe) == "" def test_element_repr(): assert repr(div()) == "" assert repr(img()) == "" def test_render_bare_elements(): assert str(div()) == "
" assert str(img()) == "" assert str(iframe()) == "" def test_positional_args_are_children(): assert str(span(img())) == "" assert str(span("hello")) == "hello" def test_keyword_args_are_attributes(): assert str(div(title="hello")) == '
' assert str(img(src="hello.png")) == '' @pytest.mark.parametrize( ("kwarg", "attribute"), [ ("foo", "foo"), ("foo_bar", "foo-bar"), ("foo__bar", "foo--bar"), ("_foo", "-foo"), ("__foo", "--foo"), ("foo_", "foo"), ("foo__", "foo"), ("_", "_"), ], ) def test_attribute_name_mangling(kwarg: str, attribute: str): assert str(div(**{kwarg: "test"})) == f'
' assert str(img(**{kwarg: "test"})) == f'' def test_boolean_attributes(): assert str(div(enabled=True)) == "
" assert str(div(enabled=False)) == "
" assert str(div(_=True)) == "
" assert str(div(_=False)) == "
" assert str(img(enabled=True)) == "" assert str(img(enabled=False)) == "" assert str(img(_=True)) == "" assert str(img(_=False)) == "" def test_attribute_values_are_escaped(): assert ( str(div(title='hello"&')) == '
' ) @pytest.mark.parametrize( "name", [ "", " ", '"', "'", "/", "=", ">", "a a", 'a"a', "a'a", "a/a", "a>a", ], ) def test_attribute_names_are_validated(name: str): with assert_raises(ValueError, match=f"Invalid attribute name: {name!r}"): div(**{name: "test"}) with assert_raises(ValueError, match=f"Invalid attribute name: {name!r}"): img(**{name: "test"}) def test_text_contend_is_escaped(): assert str(div('hello"&')) == '
hello"<world>&
' def test_indexing_adds_class_names(): assert str(div["green"]) == '
' assert str(div["green supergreen"]) == '
' assert str(div["green"]["supergreen"]) == '
' assert str(img["myclass"]) == '' assert str(img["myclass"]["otherclass"]) == '' def test_indexing_sets_hashtag_id(): assert str(div["#blue"]) == '
' assert str(div["green #blue"]) == '
' assert str(div["green #blue #red"]) == '
' assert str(img["#my-id"]) == '' def test_calling_prototype_in_element_context_adds_child_element(): with div as elem: span("hello") assert str(elem) == "
hello
" with div["myclass"] as elem: span("hello")(" again") assert str(elem) == '
hello again
' with div["myclass"] as elem: img(src="hello.png") assert str(elem) == '
' def test_elements_used_as_positional_args_are_not_added_twice(): with div as elem: div["nested"](span("hello")) assert str(elem) == dedent("""\
hello
""") with div["myclass"] as elem: div["nested"](span("hello")(" again")) assert str(elem) == dedent("""\
hello again
""") def test_context_managers_can_be_nested(): with div["outer"] as elem: with div, div["inner"]: span("hello") assert str(elem) == dedent("""\
hello
""") def test_context_manager_with_text_content(): with div as elem: text("hello") safe("") assert str(elem) == "
hello
" def test_circular_reference_raises_error_when_rendering(): elem = div() elem(elem) with assert_raises(CircularReferenceError): str(elem) with div() as elem2: div(elem2) with assert_raises(CircularReferenceError): str(elem2)