from textwrap import dedent from pytest import raises as assert_raises from minihtml import Element, Fragment, Slots, component, fragment from minihtml.tags import ( body, div, h2, head, html, img, main, p, script, style, title, ) def test_basic_component(): @component() def my_component(slots: Slots, name: str) -> Element: return div(name=name) with div["container"] as elem: my_component(name="component-name") assert str(elem) == dedent("""\
""") def test_component_slot(): @component() def my_component(slots: Slots, name: str) -> Element: with div(name=name) as elem: slots.slot() return elem with div["container"] as elem: with my_component(name="component-name"): p("slot content") assert str(elem) == dedent("""\

slot content

""") def test_named_slots(): @component(slots=("head", "main")) def my_component(slots: Slots) -> Element: with html as elem: with head: slots.slot("head") with body: with main: slots.slot("main") return elem with my_component() as comp: with comp.slot("head"): title("My website") with comp.slot("main"): p("My article") assert str(comp) == dedent("""\ My website

My article

""") def test_component_without_default_raises_if_default_slot_is_used(): @component(slots=["one"]) def my_component(slots: Slots) -> Element: with div as elem: slots.slot("one") return elem with assert_raises(KeyError): with my_component(): p("default slot content") def test_named_slots_with_default(): @component(slots=("head", "main"), default="main") def my_component(slots: Slots) -> Element: with html as elem: with head: slots.slot("head") with body: with main: slots.slot() return elem with my_component() as comp1: with comp1.slot("head"): title("My website") p("My article") assert str(comp1) == dedent("""\ My website

My article

""") with my_component() as comp2: with comp2.slot("head"): title("My website") with comp2.slot("main"): p("My article") assert str(comp1) == str(comp2) def test_slot_is_filled(): @component(slots=("icon", "main"), default="main") def my_component(slots: Slots) -> Element: with div["my-component"] as elem: if slots.is_filled("icon"): with div["icon"]: slots.slot("icon") if slots.is_filled(): with div["main"]: slots.slot() return elem with my_component() as comp1: p("My article") assert str(comp1) == dedent("""\

My article

""") with my_component() as comp2: with comp2.slot("icon"): img(src="icon.png") assert str(comp2) == dedent("""\
""") def test_slots_with_default_content(): @component(slots=("title", "content"), default="content") def my_component(slots: Slots) -> Fragment: with fragment() as f: with slots.slot("title"): h2("Default title") with slots.slot(): p("Default content") return f comp1 = my_component() with my_component() as comp2: with comp2.slot("title"): h2("My title") p("My content") assert str(comp1) == dedent("""\

Default title

Default content

""") assert str(comp2) == dedent("""\

My title

My content

""") def test_default_slot_must_be_a_valid_slot_name(): with assert_raises(ValueError, match="Can't set default without slots: 'x'"): component(default="x") with assert_raises( ValueError, match="Invalid default: 'x'. Available slots: 'a', 'b'" ): component(slots=("a", "b"), default="x") def test_stringifying_component_has_no_side_effect(): @component() def my_component(slots: Slots) -> Element: with div["my-component"] as elem: slots.slot() return elem c1 = my_component() with div as elem: str(c1) assert str(elem) == "
" def test_nested_components(): @component() def inner(slots: Slots) -> Element: with div["inner"] as elem: slots.slot() return elem @component() def outer(slots: Slots) -> Element: with div["outer"] as elem: with inner(): slots.slot() return elem with div["container"] as elem: with outer(): p("content") assert str(elem) == dedent("""\

content

""") def test_component_style_and_script_has_no_effect_outside_of_template_context(): @component( style=style(".my-component { background: #ccc }"), script=script("// script goes here"), ) def my_component(slots: Slots) -> Element: return div["my-component"] assert str(my_component()) == '
' def test_passing_component_as_argument(): @component() def my_component(slots: Slots) -> Element: return div["my-component"] with div["#container"] as elem: div(my_component()) assert str(elem) == dedent("""\
""")