from django.test import TestCase from typing import List from django_components.util.html_parser import HTMLTag, _parse_html as parse_html, set_html_attributes from .django_test_setup import setup_test_config setup_test_config({"autodiscover": False}) # This same set of tests is also found in djc_html_parser, to ensure that # this implementation can be replaced with the djc_html_parser's Rust-based implementation class TestHTMLParser(TestCase): def test_basic_transformation(self): html = "

Hello

" result, _ = set_html_attributes(html, root_attributes=["data-root"], all_attributes=["data-all"]) expected = "

Hello

" assert result == expected def test_multiple_roots(self): html = "
First
Second" result, _ = set_html_attributes(html, root_attributes=["data-root"], all_attributes=["data-all"]) expected = "
First
Second" assert result == expected def test_complex_html(self): html = """

Hello & Welcome

Article 1

Some text with bold and emphasis

Test Image
""" result, _ = set_html_attributes(html, ["data-root"], ["data-all", "data-v-123"]) expected = """

Hello & Welcome

Article 1

Some text with bold and emphasis

Test Image
""" # noqa: E501 assert result == expected def test_void_elements(self): test_cases = [ ('', ''), ('', ''), ("


", "


"), ('Test', 'Test'), ] for input_html, expected in test_cases: result, _ = set_html_attributes(input_html, ["data-root"], ["data-v-123"]) assert result == expected def test_html_head_with_meta(self): html = """ Test Page """ result, _ = set_html_attributes(html, ["data-root"], ["data-v-123"]) expected = """ Test Page """ assert result == expected def test_watch_attribute(self): html = """

Regular element

Nested element
""" result, captured = set_html_attributes(html, ["data-root"], ["data-v-123"], watch_on_attribute="data-id") expected = """

Regular element

Nested element
""" assert result == expected # Verify attribute capturing assert len(captured) == 3 # Root element should have both root and all attributes assert captured["123"] == ["data-root", "data-v-123"] # Non-root elements should only have all attributes assert captured["456"] == ["data-v-123"] assert captured["789"] == ["data-v-123"] def test_whitespace_preservation(self): html = """

Hello World

Text with spaces
""" result, _ = set_html_attributes(html, ["data-root"], ["data-all"]) expected = """

Hello World

Text with spaces
""" assert result == expected # This checks that the parser works irrespective of the main use case class TestHTMLParserInternal(TestCase): def test_parse_simple_tag(self): processed_tags = [] def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: processed_tags.append(tag) html = "
Hello
" result = parse_html(html, on_tag) self.assertEqual(result, html) self.assertEqual(len(processed_tags), 1) self.assertEqual(processed_tags[0].name, "div") def test_parse_nested_tags(self): processed_tags = [] def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: processed_tags.append((tag.name, len(tag_stack))) html = "

Hello

" result = parse_html(html, on_tag) self.assertEqual(result, html) self.assertEqual(len(processed_tags), 2) self.assertEqual(processed_tags[0], ("p", 2)) # p tag with stack depth 2 self.assertEqual(processed_tags[1], ("div", 1)) # div tag with stack depth 1 def test_parse_attributes(self): processed_tags = [] def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: processed_tags.append(tag) html = '
Hello
' result = parse_html(html, on_tag) self.assertEqual(result, html) self.assertEqual(len(processed_tags), 1) self.assertEqual(len(processed_tags[0].attrs), 2) self.assertEqual(processed_tags[0].attrs[0].key, "class") self.assertEqual(processed_tags[0].attrs[0].value, "container") self.assertEqual(processed_tags[0].attrs[1].key, "id") self.assertEqual(processed_tags[0].attrs[1].value, "main") def test_void_elements(self): processed_tags = [] def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: processed_tags.append(tag) html = '' result = parse_html(html, on_tag) self.assertEqual(result, html) self.assertEqual(len(processed_tags), 1) self.assertEqual(processed_tags[0].name, "img") self.assertEqual(processed_tags[0].attrs[0].key, "src") self.assertEqual(processed_tags[0].attrs[0].value, "test.jpg") def test_add_attr(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.add_attr("data-test", "value", quoted=True) tag.add_attr("hidden", None, quoted=False) html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, '') def test_rename_attr(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.rename_attr("class", "className") html = '
Content
' result = parse_html(html, on_tag) self.assertEqual(result, '
Content
') def test_delete_attr(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.delete_attr("id") html = '
Content
' result = parse_html(html, on_tag) self.assertEqual(result, '
Content
') def test_clear_attrs(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.clear_attrs() html = '
Content
' result = parse_html(html, on_tag) self.assertEqual(result, "
Content
") def test_add_after_clearing_attrs(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.clear_attrs() tag.add_attr("data-test", "value", quoted=True) html = '
Content
' result = parse_html(html, on_tag) self.assertEqual(result, '
Content
') def test_insert_content(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.insert_content("Start ", 0) tag.insert_content(" End", -1) html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, "
Start Content End
") def test_clear_content(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.clear_content() html = "
Original content
" result = parse_html(html, on_tag) self.assertEqual(result, "
") def test_replace_content(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.replace_content("New content") html = "
Original content
" result = parse_html(html, on_tag) self.assertEqual(result, "
New content
") def test_prepend_append(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.prepend("Before ") tag.append(" after") html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, "Before
Content
after") def test_wrap(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.wrap('
', "
") html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, '
Content
') def test_unwrap(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: if tag.name == "span": tag.unwrap() html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, "
Content
") def test_rename_tag(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: tag.rename_tag("article") html = "
Content
" result = parse_html(html, on_tag) self.assertEqual(result, "
Content
") def test_get_attr_has_attr(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: assert tag.has_attr("class") assert not tag.has_attr("id") attr = tag.get_attr("class") assert attr is not None and attr.value == "test" assert tag.get_attr("id") is None html = '
Content
' result = parse_html(html, on_tag) self.assertEqual(result, html) def test_tag_manipulation_complex(self): def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: if tag.name == "div": # Test add_attr tag.add_attr("data-new", "value", quoted=True) # Test rename_attr tag.rename_attr("class", "className") # Test delete_attr tag.delete_attr("id") # Test insert_content tag.insert_content("Start", 0) tag.insert_content("End", -1) # Test wrap tag.wrap("
", "
") elif tag.name == "p": # Test get_attr and has_attr assert tag.has_attr("class") attr = tag.get_attr("class") assert attr is not None and attr.value == "inner" # Test clear_attrs tag.clear_attrs() # Test clear_content and replace_content tag.clear_content() tag.replace_content("New content") # Test prepend and append tag.prepend("Before ") tag.append(" after") # Test rename_tag tag.rename_tag("article") # Test unwrap tag.unwrap() html = '

Original content

' expected = '
StartBefore New content afterEnd
' # noqa: E501 result = parse_html(html, on_tag) self.assertEqual(result, expected) def test_complex_html(self): processed_tags = [] def on_tag(tag: HTMLTag, tag_stack: List[HTMLTag]) -> None: processed_tags.append(tag) if tag.name == "body": # Test attribute manipulation tag.add_attr("data-modified", "true", quoted=True) tag.rename_attr("class", "className") elif tag.name == "div": # Test content manipulation tag.insert_content("", 0) tag.wrap('
', "
") elif tag.name == "p": # Test attribute without value tag.add_attr("hidden", None, quoted=False) html = """ Complex Test */ const template = `
${value}
`; console.log(''); that should be preserved ]]>
Test Image

Hello World!

""" expected = """ Complex Test */ const template = `
${value}
`; console.log(''); that should be preserved ]]>
Test Image
""" result = parse_html(html, on_tag) self.assertEqual(result, expected) # Verify the structure of processed tags self.assertEqual(len(processed_tags), 12) # Count all non-void elements # Verify specific tag attributes html_tag = next(tag for tag in processed_tags if tag.name == "html") self.assertEqual(len(html_tag.attrs), 2) self.assertEqual(html_tag.attrs[0].key, "lang") self.assertEqual(html_tag.attrs[0].value, "en") self.assertEqual(html_tag.attrs[1].key, "data-theme") self.assertEqual(html_tag.attrs[1].value, "light") # Verify void elements img_tag = next(tag for tag in processed_tags if tag.name == "img") self.assertEqual(len(img_tag.attrs), 2) self.assertEqual(img_tag.attrs[0].key, "src") self.assertEqual(img_tag.attrs[0].value, "test.jpg") # Verify attribute without value body_tag = next(tag for tag in processed_tags if tag.name == "body") data_loaded_attr = next(attr for attr in body_tag.attrs if attr.key == "data-loaded") self.assertIsNone(data_loaded_attr.value) # Verify modified attributes self.assertTrue(any(attr.key == "data-modified" and attr.value == "true" for attr in body_tag.attrs)) self.assertTrue(any(attr.key == "className" and attr.value == "main" for attr in body_tag.attrs)) # Verify p tag modifications p_tag = next(tag for tag in processed_tags if tag.name == "p") self.assertTrue(any(attr.key == "hidden" and attr.value is None for attr in p_tag.attrs))