diff --git a/.github/workflows/stylecheck.yml b/.github/workflows/stylecheck.yml
index 8becc50..f94c46d 100644
--- a/.github/workflows/stylecheck.yml
+++ b/.github/workflows/stylecheck.yml
@@ -27,3 +27,20 @@ jobs:
run: mypy --strict html_to_django
- name: Pycodestyle
run: pycodestyle --max-line-length 120 html_to_django
+ stylecheck-tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup Python 3.12
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.12
+ - name: Install Dependencies
+ run: |
+ pip install ".[test]"
+ mypy tests || true
+ mypy --install-types --non-interactive
+ - name: MyPy
+ run: mypy --strict tests
+ - name: Pycodestyle
+ run: pycodestyle --max-line-length 120 tests
diff --git a/html_to_django/formatters/attr_formatter.py b/html_to_django/formatters/attr_formatter.py
index e0d237e..cccde21 100644
--- a/html_to_django/formatters/attr_formatter.py
+++ b/html_to_django/formatters/attr_formatter.py
@@ -3,6 +3,6 @@ from bs4 import BeautifulSoup
def format(soup: BeautifulSoup) -> None:
for element in soup.find_all(attrs={"dj-attr": True}):
- tag, value = element.get("dj-attr").split(";")
- element[tag] = f"{{{{ {value} }}}}"
+ for tag, value in [attr.split(";") for attr in element["dj-attr"].split("~")]:
+ element[tag] = f"{{{{ {value} }}}}"
del element.attrs["dj-attr"]
diff --git a/html_to_django/formatters/style_formatter.py b/html_to_django/formatters/style_formatter.py
index 7ad95c5..9f04db6 100644
--- a/html_to_django/formatters/style_formatter.py
+++ b/html_to_django/formatters/style_formatter.py
@@ -21,12 +21,18 @@ def parse_style(style: str) -> dict[str, str]:
def style_to_string(style: dict[str, str]) -> str:
- return ";".join([f"{name}:{value}" for name, value in style.items()])
+ result = ";".join([f"{name}:{value}" for name, value in style.items()])
+ if len(style) != 0:
+ result += ";"
+ return result
def format(soup: BeautifulSoup) -> None:
for element in soup.find_all(attrs={"dj-style": True}):
- style = parse_style(element["style"])
+ if element.has_attr("style"):
+ style = parse_style(element["style"])
+ else:
+ style = {}
dj_style = element.get("dj-style")
for name, value, value_type in [declaration.split(";") for declaration in dj_style.split("~") if declaration]:
style[name] = f"{{{{ {value} }}}}{value_type}"
diff --git a/html_to_django/formatters/style_raw_formatter.py b/html_to_django/formatters/style_raw_formatter.py
index ea2d2ea..fd39c73 100644
--- a/html_to_django/formatters/style_raw_formatter.py
+++ b/html_to_django/formatters/style_raw_formatter.py
@@ -4,7 +4,10 @@ from .style_formatter import parse_style, style_to_string
def format(soup: BeautifulSoup) -> None:
for element in soup.find_all(attrs={"dj-style-raw": True}):
- style = parse_style(element["style"])
+ if element.has_attr("style"):
+ style = parse_style(element["style"])
+ else:
+ style = {}
dj_style = element.get("dj-style-raw")
for name, value in [declaration.split(";") for declaration in dj_style.split("~") if declaration]:
style[name] = value
diff --git a/tests/formatters/tests_attr_formatter.py b/tests/formatters/tests_attr_formatter.py
new file mode 100644
index 0000000..fde39f2
--- /dev/null
+++ b/tests/formatters/tests_attr_formatter.py
@@ -0,0 +1,23 @@
+import unittest
+from bs4 import BeautifulSoup
+from html_to_django.formatters.attr_formatter import format
+
+
+class TestAttrFormatter(unittest.TestCase):
+ def test_format(self) -> None:
+ soup = BeautifulSoup('
', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_multiple(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_overwrite_existing(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_block_formatter.py b/tests/formatters/tests_block_formatter.py
index 7ba9eda..f1fe269 100644
--- a/tests/formatters/tests_block_formatter.py
+++ b/tests/formatters/tests_block_formatter.py
@@ -4,17 +4,20 @@ from html_to_django.formatters import block_formatter
class BlockTests(TestCase):
- def test_basic(self):
+ def test_basic(self) -> None:
soup = BeautifulSoup('', "html.parser")
block_formatter.format(soup)
- self.assertEqual("{% block content %}{% endblock %}", str(soup))
-
- def test_with_content(self):
+ expected_result = "{% block content %}{% endblock %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
soup = BeautifulSoup('Test
', "html.parser")
block_formatter.format(soup)
- self.assertEqual("{% block content %}Test{% endblock %}", str(soup))
-
- def test_with_block_inside(self):
+ expected_result = "{% block content %}Test{% endblock %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_block_inside(self) -> None:
soup = BeautifulSoup('', "html.parser")
block_formatter.format(soup)
- self.assertEqual("{% block content %}{% block inner %}Test{% endblock %}{% endblock %}", str(soup))
+ expected_result = "{% block content %}{% block inner %}Test{% endblock %}{% endblock %}"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_command_formatter.py b/tests/formatters/tests_command_formatter.py
new file mode 100644
index 0000000..e12f274
--- /dev/null
+++ b/tests/formatters/tests_command_formatter.py
@@ -0,0 +1,17 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import command_formatter
+
+
+class CommandTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ command_formatter.format(soup)
+ expected_result = "{% command_name %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ command_formatter.format(soup)
+ expected_result = "{% command_name %}"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_for_formatter.py b/tests/formatters/tests_for_formatter.py
new file mode 100644
index 0000000..e53467e
--- /dev/null
+++ b/tests/formatters/tests_for_formatter.py
@@ -0,0 +1,24 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import for_formatter
+
+
+class ForTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ for_formatter.format(soup)
+ expected_result = "{% for item in list %}{% endfor %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ for_formatter.format(soup)
+ expected_result = "{% for item in list %}Test{% endfor %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_for_inside(self) -> None:
+ soup = BeautifulSoup('',
+ "html.parser")
+ for_formatter.format(soup)
+ expected_result = "{% for item in list %}{% for subitem in sublist %}Test{% endfor %}{% endfor %}"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_for_wrap_formatter.py b/tests/formatters/tests_for_wrap_formatter.py
new file mode 100644
index 0000000..29b697c
--- /dev/null
+++ b/tests/formatters/tests_for_wrap_formatter.py
@@ -0,0 +1,25 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import for_wrap_formatter
+
+
+class ForWrapTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ for_wrap_formatter.format(soup)
+ expected_result = "{% for item in list %}{% endfor %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ for_wrap_formatter.format(soup)
+ expected_result = "{% for item in list %}Test
{% endfor %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_for_wrap_inside(self) -> None:
+ soup = BeautifulSoup('',
+ "html.parser")
+ for_wrap_formatter.format(soup)
+ expected_result = ("{% for item in list %}{% for subitem in sublist %}
Test
{% endfor %}"
+ "
{% endfor %}")
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_if_formatter.py b/tests/formatters/tests_if_formatter.py
new file mode 100644
index 0000000..4f2a6d4
--- /dev/null
+++ b/tests/formatters/tests_if_formatter.py
@@ -0,0 +1,30 @@
+import unittest
+from bs4 import BeautifulSoup
+from html_to_django.formatters.if_formatter import format
+
+
+class TestIfFormatter(unittest.TestCase):
+ def test_format(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ format(soup)
+ expected_result = "{% if condition %}Test{% endif %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_elif(self) -> None:
+ soup = BeautifulSoup('Test
Test2
', "html.parser")
+ format(soup)
+ expected_result = "{% if condition1 %}Test{% elif condition2 %}Test2{% endif %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_else(self) -> None:
+ soup = BeautifulSoup('Test
Test2
', "html.parser")
+ format(soup)
+ expected_result = "{% if condition1 %}Test{% else %}Test2{% endif %}"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_elif_and_else(self) -> None:
+ soup = BeautifulSoup('Test
Test2
'
+ 'Test3
', "html.parser")
+ format(soup)
+ expected_result = "{% if condition1 %}Test{% elif condition2 %}Test2{% else %}Test3{% endif %}"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_include_formatter.py b/tests/formatters/tests_include_formatter.py
new file mode 100644
index 0000000..46512aa
--- /dev/null
+++ b/tests/formatters/tests_include_formatter.py
@@ -0,0 +1,17 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import include_formatter
+
+
+class IncludeTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ include_formatter.format(soup)
+ expected_result = '{% include "template_name" %}
'
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ include_formatter.format(soup)
+ expected_result = '{% include "template_name" %}
'
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_remove_default_formatter.py b/tests/formatters/tests_remove_default_formatter.py
new file mode 100644
index 0000000..fc55759
--- /dev/null
+++ b/tests/formatters/tests_remove_default_formatter.py
@@ -0,0 +1,29 @@
+import unittest
+from bs4 import BeautifulSoup
+from bs4.formatter import HTMLFormatter
+
+from html_to_django.formatters.remove_default_formatter import format
+
+
+class TestFormatFunction(unittest.TestCase):
+ # The strings here look weird because of the indentation and line breaks, but it must be this way
+ def test_basic(self) -> None:
+ html = """
+
+
+
+ Title
+
+
+
+ Test
+
+
+
+
+ """
+ soup = BeautifulSoup(html, "html.parser")
+ format(soup)
+ expected_result = "\n Test\n
\n"
+ self.assertEqual(expected_result, soup.prettify(formatter=HTMLFormatter(indent=4)))
diff --git a/tests/formatters/tests_static_formatter.py b/tests/formatters/tests_static_formatter.py
new file mode 100644
index 0000000..9915a6d
--- /dev/null
+++ b/tests/formatters/tests_static_formatter.py
@@ -0,0 +1,17 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import static_formatter
+
+
+class StaticTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('
', "html.parser")
+ static_formatter.format(soup)
+ expected_result = '
'
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ static_formatter.format(soup)
+ expected_result = 'Test
'
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_style_formatter.py b/tests/formatters/tests_style_formatter.py
new file mode 100644
index 0000000..36b63c3
--- /dev/null
+++ b/tests/formatters/tests_style_formatter.py
@@ -0,0 +1,76 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters.style_formatter import parse_style, style_to_string, format
+
+
+class TestParseStyle(TestCase):
+ def test_parse_style(self) -> None:
+ style = "color:red;background-color:blue;width:100px"
+ expected_result = {
+ "color": "red",
+ "background-color": "blue",
+ "width": "100px"
+ }
+ self.assertEqual(expected_result, parse_style(style))
+
+ def test_parse_style_ending_semicolon(self) -> None:
+ style = "color:red;background-color:blue;width:100px;"
+ expected_result = {
+ "color": "red",
+ "background-color": "blue",
+ "width": "100px"
+ }
+ self.assertEqual(expected_result, parse_style(style))
+
+ def test_parse_style_with_spaces(self) -> None:
+ style = "color: red ; background-color: blue ; width: 100px"
+ expected_result = {
+ "color": "red",
+ "background-color": "blue",
+ "width": "100px"
+ }
+ self.assertEqual(expected_result, parse_style(style))
+
+ def test_parse_style_empty(self) -> None:
+ style = ""
+ expected_result: dict[str, str] = {}
+ self.assertEqual(expected_result, parse_style(style))
+
+
+class TestStyleToString(TestCase):
+ def test_style_to_string(self) -> None:
+ style = {
+ "color": "red",
+ "background-color": "blue",
+ "width": "100px"
+ }
+ expected_result = "color:red;background-color:blue;width:100px;"
+ self.assertEqual(expected_result, style_to_string(style))
+
+ def test_style_to_string_with_empty_dict(self) -> None:
+ style: dict[str, str] = {}
+ expected_result = ""
+ self.assertEqual(expected_result, style_to_string(style))
+
+
+class TestFormat(TestCase):
+ def test_format(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ('')
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_overwriting_existing_style(self) -> None:
+ soup = BeautifulSoup('',
+ "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_no_style(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_style_raw_formatter.py b/tests/formatters/tests_style_raw_formatter.py
new file mode 100644
index 0000000..875bf10
--- /dev/null
+++ b/tests/formatters/tests_style_raw_formatter.py
@@ -0,0 +1,23 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters.style_raw_formatter import format
+
+
+class TestStyleRawFormat(TestCase):
+ def test_format(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_existing_style(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
+
+ def test_format_with_no_style(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ format(soup)
+ expected_result = ''
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_unwrap_formatter.py b/tests/formatters/tests_unwrap_formatter.py
new file mode 100644
index 0000000..b9adaf8
--- /dev/null
+++ b/tests/formatters/tests_unwrap_formatter.py
@@ -0,0 +1,23 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import unwrap_formatter
+
+
+class UnwrapTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ unwrap_formatter.format(soup)
+ expected_result = "Test
"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_empty(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ unwrap_formatter.format(soup)
+ expected_result = ""
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_unwrap_inside(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ unwrap_formatter.format(soup)
+ expected_result = "Inner Test"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/formatters/tests_var_formatter.py b/tests/formatters/tests_var_formatter.py
new file mode 100644
index 0000000..00627d3
--- /dev/null
+++ b/tests/formatters/tests_var_formatter.py
@@ -0,0 +1,17 @@
+from unittest import TestCase
+from bs4 import BeautifulSoup
+from html_to_django.formatters import var_formatter
+
+
+class VarTests(TestCase):
+ def test_basic(self) -> None:
+ soup = BeautifulSoup('', "html.parser")
+ var_formatter.format(soup)
+ expected_result = "{{ item }}
"
+ self.assertEqual(expected_result, str(soup))
+
+ def test_with_content(self) -> None:
+ soup = BeautifulSoup('Test
', "html.parser")
+ var_formatter.format(soup)
+ expected_result = "{{ item }}
"
+ self.assertEqual(expected_result, str(soup))
diff --git a/tests/tests_converter.py b/tests/tests_converter.py
index a18a505..29838a9 100644
--- a/tests/tests_converter.py
+++ b/tests/tests_converter.py
@@ -6,7 +6,7 @@ from html_to_django import converter
class ConverterTests(TestCase):
- def test_convert_file(self):
+ def test_convert_file(self) -> None:
shutil.copytree(
os.path.abspath("tests/test_project"),
os.path.abspath("tests/test_project_test_result"),