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('
Test
', "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('
Test
', + "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('
Test
', + "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('
Test
', "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('
Inner Test
', "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"),