Skip to content

Instantly share code, notes, and snippets.

@davepeck
Created June 7, 2026 23:33
Show Gist options
  • Select an option

  • Save davepeck/32c096daeab71d2040ff396c61da58bf to your computer and use it in GitHub Desktop.

Select an option

Save davepeck/32c096daeab71d2040ff396c61da58bf to your computer and use it in GitHub Desktop.
For discussion: proposed new parser tests
import pytest
from .htmlspec import Namespace # proposed; new -- has .HTML, .SVG, and .MATH
from .parser import TemplateParser
from .template_utils import TemplateRef
from .tnodes import (
TComponent,
TElement,
TInterpolatedAttribute,
TLiteralAttribute,
TSpreadAttribute,
TTemplatedAttribute,
)
def test_void_with_no_attributes_no_whitespace():
node = TemplateParser.parse(t"<br/>")
assert node == TElement("br")
def test_void_with_no_attributes_whitespace():
node = TemplateParser.parse(t"<br />")
assert node == TElement("br")
def test_void_with_unquoted_end_attribute_no_whitespace():
node = TemplateParser.parse(t"<br x=a/>")
assert node == TElement("br", attrs=(TLiteralAttribute(name="x", value="a/"),))
def test_void_with_unquoted_end_attribute_whitespace():
node = TemplateParser.parse(t"<br x=a />")
assert node == TElement("br", attrs=(TLiteralAttribute(name="x", value="a"),))
def test_void_with_interpolated_end_attribute_no_whitespace():
a = "wow"
node = TemplateParser.parse(t"<br x={a}/>")
assert node == TElement(
"br",
attrs=(
TTemplatedAttribute(
name="x", value_ref=TemplateRef(strings=("", "/"), i_indexes=(0,))
),
),
)
def test_void_with_interpolated_end_attribute_whitespace():
a = "wow"
node = TemplateParser.parse(t"<br x={a} />")
assert node == TElement(
"br", attrs=(TInterpolatedAttribute(name="x", value_i_index=0),)
)
def test_void_with_quoted_end_attribute_no_whitespace():
node = TemplateParser.parse(t'<br x="a"/>')
assert node == TElement("br", attrs=(TLiteralAttribute(name="x", value="a"),))
def test_void_with_boolean_end_attribute_no_whitespace():
node = TemplateParser.parse(t"<br x/>")
assert node == TElement("br", attrs=(TLiteralAttribute(name="x", value=None),))
def test_void_with_spread_attribute_no_whitespace():
attrs = {"x": "a"}
node = TemplateParser.parse(t"<br {attrs}/>")
assert node == TElement("br", attrs=(TSpreadAttribute(i_index=0),))
def test_non_void_with_no_attributes_no_whitespace():
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div/>")
def test_non_void_with_no_attributes_whitespace():
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div />")
def test_non_void_with_unquoted_end_attribute_no_whitespace():
with pytest.raises(ValueError):
# Raises because this is an open tag without a matching close tag
_ = TemplateParser.parse(t"<div x=a/>")
def test_non_void_with_unquoted_end_attribute_whitespace():
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div x=a />")
def test_non_void_with_unquoted_end_attribute_no_whitespace_closed():
node = TemplateParser.parse(t"<div x=a/></div>")
assert node == TElement("div", attrs=(TLiteralAttribute(name="x", value="a/"),))
def test_non_void_with_interpolated_end_attribute_no_whitespace():
a = "wow"
with pytest.raises(ValueError):
# Raises because this is an open tag without a matching close tag
_ = TemplateParser.parse(t"<div x={a}/>")
def test_non_void_with_interpolated_end_attribute_no_whitespace_closed():
a = "wow"
node = TemplateParser.parse(t"<div x={a}/></div>")
assert node == TElement(
"div",
attrs=(
TTemplatedAttribute(
name="x", value_ref=TemplateRef(strings=("", "/"), i_indexes=(0,))
),
),
)
def test_non_void_with_quoted_end_attribute_no_whitespace():
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t'<div x="a"/>')
def test_non_void_with_boolean_end_attribute_no_whitespace():
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div x/>")
def test_non_void_with_spread_attribute_no_whitespace():
attrs = {"x": "a"}
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div {attrs}/>")
def test_non_void_with_interpolated_end_attribute_whitespace():
a = "wow"
with pytest.raises(ValueError):
# Raises because this is not a void tag, so this syntax is illegal
_ = TemplateParser.parse(t"<div x={a} />")
def test_non_void_with_multiple_attributes_no_whitespace_closed():
node = TemplateParser.parse(t"<div y=b x=a/></div>")
assert node == TElement(
"div",
attrs=(
TLiteralAttribute(name="y", value="b"),
TLiteralAttribute(name="x", value="a/"),
),
)
def test_component_with_unquoted_end_attribute_no_whitespace():
def Component(x: str):
pass
with pytest.raises(ValueError):
# Raises because this is an open tag without a matching close tag
_ = TemplateParser.parse(t"<{Component} x=a/>")
def test_component_with_unquoted_end_attribute_whitespace():
def Component(x: str):
pass
node = TemplateParser.parse(t"<{Component} x=a />")
assert node == TComponent(
start_i_index=0, attrs=(TLiteralAttribute(name="x", value="a"),)
)
def test_component_with_unquoted_end_attribute_no_whitespace_closed():
def Component(x: str):
pass
node = TemplateParser.parse(t"<{Component} x=a/></{Component}>")
assert node == TComponent(
start_i_index=0,
end_i_index=1,
attrs=(TLiteralAttribute(name="x", value="a/"),),
)
def test_component_with_interpolated_end_attribute_no_whitespace():
def Component(x: str):
pass
a = "wow"
with pytest.raises(ValueError):
# Raises because this is an open tag without a corresponding close
_ = TemplateParser.parse(t"<{Component} x={a}/>")
def test_component_with_interpolated_end_attribute_whitespace():
def Component(x: str):
pass
a = "wow"
node = TemplateParser.parse(t"<{Component} x={a} />")
assert node == TComponent(
start_i_index=0, attrs=(TInterpolatedAttribute(name="x", value_i_index=1),)
)
def test_component_with_interpolated_end_attribute_no_whitespace_closed():
def Component(x: str):
pass
a = "wow"
node = TemplateParser.parse(t"<{Component} x={a}/></{Component}>")
assert node == TComponent(
start_i_index=0,
end_i_index=2,
attrs=(
TTemplatedAttribute(
name="x", value_ref=TemplateRef(strings=("", "/"), i_indexes=(1,))
),
),
)
def test_component_with_no_attributes_no_whitespace():
def Component():
pass
node = TemplateParser.parse(t"<{Component}/>")
assert node == TComponent(start_i_index=0)
def test_component_with_no_attributes_whitespace():
def Component():
pass
node = TemplateParser.parse(t"<{Component} />")
assert node == TComponent(start_i_index=0)
def test_component_with_quoted_end_attribute_no_whitespace():
def Component(x: str):
pass
node = TemplateParser.parse(t'<{Component} x="a"/>')
assert node == TComponent(
start_i_index=0, attrs=(TLiteralAttribute(name="x", value="a"),)
)
def test_component_with_boolean_end_attribute_no_whitespace():
def Component(x: bool = False):
pass
node = TemplateParser.parse(t"<{Component} x/>")
assert node == TComponent(
start_i_index=0, attrs=(TLiteralAttribute(name="x", value=None),)
)
def test_component_with_spread_end_attribute_no_whitespace():
def Component(**kwargs):
pass
attrs = {"x": "a"}
node = TemplateParser.parse(t"<{Component} {attrs}/>")
assert node == TComponent(start_i_index=0, attrs=(TSpreadAttribute(i_index=1),))
#
# SVG Behavior
#
def test_svg_with_no_attributes_no_whitespace():
node = TemplateParser.parse(t"<svg><rect/></svg>")
assert node == TElement("svg", children=(TElement("rect"),))
def test_svg_with_no_attributes_whitespace():
node = TemplateParser.parse(t"<svg><rect /></svg>")
assert node == TElement("svg", children=(TElement("rect"),))
def test_svg_with_unquoted_end_attribute_no_whitespace():
with pytest.raises(ValueError):
# Raises because the unquoted value absorbs the "/", leaving <rect> open
_ = TemplateParser.parse(t"<svg><rect x=a/></svg>")
def test_svg_with_unquoted_end_attribute_whitespace():
node = TemplateParser.parse(t"<svg><rect x=a /></svg>")
assert node == TElement(
"svg",
children=(TElement("rect", attrs=(TLiteralAttribute(name="x", value="a"),)),),
)
def test_svg_with_unquoted_end_attribute_no_whitespace_closed():
node = TemplateParser.parse(t"<svg><rect x=a/></rect></svg>")
assert node == TElement(
"svg",
children=(TElement("rect", attrs=(TLiteralAttribute(name="x", value="a/"),)),),
)
def test_svg_with_quoted_end_attribute_no_whitespace():
node = TemplateParser.parse(t'<svg><rect x="a"/></svg>')
assert node == TElement(
"svg",
children=(TElement("rect", attrs=(TLiteralAttribute(name="x", value="a"),)),),
)
def test_svg_with_boolean_end_attribute_no_whitespace():
node = TemplateParser.parse(t"<svg><rect x/></svg>")
assert node == TElement(
"svg",
children=(TElement("rect", attrs=(TLiteralAttribute(name="x", value=None),)),),
)
def test_svg_with_spread_end_attribute_no_whitespace():
attrs = {"x": "a"}
node = TemplateParser.parse(t"<svg><rect {attrs}/></svg>")
assert node == TElement(
"svg",
children=(TElement("rect", attrs=(TSpreadAttribute(i_index=0),)),),
)
def test_svg_with_interpolated_end_attribute_no_whitespace():
a = "wow"
with pytest.raises(ValueError):
# Raises because the interpolated value absorbs the "/", leaving <rect> open
_ = TemplateParser.parse(t"<svg><rect x={a}/></svg>")
def test_svg_with_interpolated_end_attribute_whitespace():
a = "wow"
node = TemplateParser.parse(t"<svg><rect x={a} /></svg>")
assert node == TElement(
"svg",
children=(
TElement(
"rect",
attrs=(TInterpolatedAttribute(name="x", value_i_index=0),),
),
),
)
def test_svg_with_interpolated_end_attribute_no_whitespace_closed():
a = "wow"
node = TemplateParser.parse(t"<svg><rect x={a}/></rect></svg>")
assert node == TElement(
"svg",
children=(
TElement(
"rect",
attrs=(
TTemplatedAttribute(
name="x",
value_ref=TemplateRef(strings=("", "/"), i_indexes=(0,)),
),
),
),
),
)
def test_svg_element_at_html_root_no_whitespace():
with pytest.raises(ValueError):
# Raises because at the HTML root <rect> is an unknown, non-void element
_ = TemplateParser.parse(t"<rect/>")
def test_svg_element_at_svg_root_no_whitespace():
# SPECULATIVE: for this to make sense, I assume `TElement` needs to hold on
# to a namespace too (`TElement.ns`, with a default of `Namespace.HTML`)
#
# We support Namespace.HTML, Namespace.SVG, and Namespace.MATH, I guess,
# since those are the only true three?
#
# And how are component return values (say, `t"<rect/>"`) interpreted?
# I suppose components don't declare a namespace; they inherit their
# call site's? ... on the *render* side, we are context-aware for
# component return values; i suppose we'd need this to be true on the
# parse side, too, that is `_process_template(...)` would need to
# take a context.
node = TemplateParser.parse(t"<rect/>", namespace=Namespace.SVG)
assert node == TElement("rect")
# To consider: foreignObject, back to HTML.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment