diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..b9e41de
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+source = src/djlint
+branch = True
+
+
+[report]
+show_missing = True
+skip_covered = True
+omit =
+ */test*
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..54f9758
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,27 @@
+name: test
+on: [push]
+
+jobs:
+ build:
+ strategy:
+ matrix:
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ python-version: [3.6, 3.7, 3.8, 3.9]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - name: checkout
+ uses: actions/checkout@v2
+ - name: setup python ${{ matrix.python-version }} on ${{ matrix.os }}
+ uses: actions/setup-python@v1
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: install deps
+ run: python -m pip install tox
+ - name: test
+ run: tox -p
+ - name: upload cov
+ uses: codecov/codecov-action@v1
+ with:
+ files: ./coverage.xml
+ fail_ci_if_error: true
+ verbose: true
diff --git a/.gitignore b/.gitignore
index ba2a201..adca2b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-
+report.xml
# Created by https://www.toptal.com/developers/gitignore/api/macos,python
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,python
diff --git a/setup.py b/setup.py
index 58cfc0b..09f13d2 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,12 @@ def long_description():
)
+test_deps = ["coverage", "pytest", "pytest-xdist", "pytest-cov"]
+
+extras = {
+ "test": test_deps,
+}
+
setup(
name="djlint",
version="0.0.8",
@@ -35,14 +41,13 @@ setup(
package_dir={"": "src"},
packages=find_packages(where="src"),
python_requires=">=3.6",
- install_requires=["click>=7.1.2", "pyyaml>=5.4.1"],
+ install_requires=["click>=7.1.2", "pyyaml>=5.4.1", "colorama>=0.4.3"],
test_suite="tests.test_djlint",
- extras_require={
- "colorama": ["colorama>=0.4.3"],
- },
entry_points={
"console_scripts": [
"djlint=djlint:main",
]
},
+ tests_require=test_deps,
+ extras_require=extras,
)
diff --git a/src/djlint/__init__.py b/src/djlint/__init__.py
index 14d68bd..1076e55 100644
--- a/src/djlint/__init__.py
+++ b/src/djlint/__init__.py
@@ -30,12 +30,12 @@ def lint_file(this_file: Path):
html = this_file.read_text(encoding="utf8")
# build list of line ends for file
- line_ends = [m.end() for m in re.finditer(r".*\n", html)]
+ line_ends = [m.end() for m in re.finditer(r"(?:.*\n)|(?:[^\n]+$)", html)]
def get_line(start):
"""Get the line number and index of match."""
for index, value in enumerate(line_ends):
- if value >= start:
+ if value > start:
line_start_index = (line_ends[index - 1] if index > 0 else 0) - 1
return "%d:%d" % (index + 1, start - line_start_index)
@@ -69,7 +69,7 @@ def get_src(src: Path, extension=None):
paths = list(src.glob(r"**/*.%s" % extension))
if len(paths) == 0:
- echo("No files to lint! 😢")
+ echo(Fore.BLUE + "No files to lint! 😢")
return []
return paths
@@ -124,7 +124,9 @@ def main(src: str, extension: str):
+ Style.RESET_ALL
)
error_count += len(errors)
- for message in sorted(errors, key=lambda x: x["line"]):
+ for message in sorted(
+ errors, key=lambda x: int(x["line"].split(":")[0])
+ ):
error = bool(message["code"][:1] == "E")
echo(
"{} {} {} {} {}".format(
diff --git a/src/djlint/rules.yaml b/src/djlint/rules.yaml
index 7b6932e..5ae4f16 100644
--- a/src/djlint/rules.yaml
+++ b/src/djlint/rules.yaml
@@ -26,7 +26,7 @@
name: W005
message: Html tag should have lang attribute.
patterns:
- -
+ -
- rule:
name: W006
message: Img tag should have alt, height and width attributes.
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/bad.html.dj2 b/tests/bad.html.dj2
new file mode 100644
index 0000000..1a18979
--- /dev/null
+++ b/tests/bad.html.dj2
@@ -0,0 +1 @@
+
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..6e18a59
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,15 @@
+import tempfile
+
+import pytest
+from click.testing import CliRunner
+
+
+@pytest.fixture
+def runner():
+ yield CliRunner()
+
+
+@pytest.fixture
+def tmp_file():
+ with tempfile.NamedTemporaryFile() as tmp:
+ yield tmp
diff --git a/tests/test_djlint.py b/tests/test_djlint.py
index e69de29..56c2e3f 100644
--- a/tests/test_djlint.py
+++ b/tests/test_djlint.py
@@ -0,0 +1,202 @@
+"""Djlint Tests.
+
+run::
+
+ coverage erase; coverage run -m pytest; coverage report -m
+
+or::
+
+ tox
+"""
+
+
+from src.djlint import main as djlint
+
+
+def test_help(runner):
+ result = runner.invoke(djlint, ["-h"])
+ assert result.exit_code == 0
+ assert "Djlint django template files." in result.output
+
+
+def test_bad_args(runner):
+ result = runner.invoke(djlint, ["-a"])
+ assert result.exit_code == 2
+ assert "Error: No such option: -a" in result.output
+
+ result = runner.invoke(djlint, ["--aasdf"])
+ assert result.exit_code == 2
+ assert "Error: No such option: --aasdf" in result.output
+
+
+def test_bad_file(runner):
+ result = runner.invoke(djlint, ["not_a_file.html"])
+ assert result.exit_code == 2
+ assert "Path 'not_a_file.html' does not exist." in result.output
+
+
+def test_good_file(runner):
+ result = runner.invoke(djlint, ["tests/bad.html"])
+ assert result.exit_code == 0
+ assert "tests/bad.html" in result.output
+
+
+def test_bad_path(runner):
+ result = runner.invoke(djlint, ["tests/nowhere"])
+ assert result.exit_code == 2
+ assert "Path 'tests/nowhere' does not exist." in result.output
+
+
+def test_good_path_with_ext(runner):
+ result = runner.invoke(djlint, ["tests/", "-e", "html"])
+ assert result.exit_code == 0
+ assert "tests/bad.html" in result.output
+
+ result = runner.invoke(djlint, ["tests/", "--extension", "html*"])
+ assert result.exit_code == 0
+ assert "tests/bad.html" in result.output
+ assert "tests/bad.html.dj" in result.output
+
+
+def test_good_path_with_bad_ext(runner):
+ result = runner.invoke(djlint, ["tests/", "-e", "html.alphabet"])
+ assert result.exit_code == 0
+ assert "No files to lint!" in result.output
+
+
+def test_empty_file(runner, tmp_file):
+ tmp_file.write(b"")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+
+
+def test_E001(runner, tmp_file):
+ tmp_file.write(b"{{test }}\n{% test%}")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "E001 1:" in result.output
+ assert "E001 2:4" in result.output
+
+
+def test_E002(runner, tmp_file):
+ tmp_file.write(b"{% extends 'this' %}")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "E002 1:" in result.output
+
+
+def test_W003(runner, tmp_file):
+ tmp_file.write(b"{% endblock %}")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "W003 1:" in result.output
+
+
+def test_W004(runner, tmp_file):
+ tmp_file.write(b'')
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "W004 1:" in result.output
+
+
+def test_W005(runner, tmp_file):
+ tmp_file.write(b"\n")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "W005 2:" in result.output
+
+
+def test_W006(runner, tmp_file):
+ tmp_file.write(b"")
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "W006 1:" in result.output
+
+
+def test_W007(runner, tmp_file):
+ tmp_file.write(b'')
+ tmp_file.seek(0)
+ result = runner.invoke(djlint, [tmp_file.name])
+ assert result.exit_code == 0
+ assert "W007 1:" in result.output
+
+
+def test_W008(runner, tmp_file):
+ tmp_file.write(b"
") + tmp_file.seek(0) + result = runner.invoke(djlint, [tmp_file.name]) + assert result.exit_code == 0 + assert "W014 1:" in result.output + + +def test_W015(runner, tmp_file): + tmp_file.write(b"
") + tmp_file.seek(0) + result = runner.invoke(djlint, [tmp_file.name]) + assert result.exit_code == 0 + assert "W015 1:" in result.output + + +def test_W016(runner, tmp_file): + tmp_file.write(b"\nstuff\n") + tmp_file.seek(0) + result = runner.invoke(djlint, [tmp_file.name]) + assert result.exit_code == 0 + assert "W016 1:" in result.output diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..3da4106 --- /dev/null +++ b/tox.ini @@ -0,0 +1,28 @@ +[tox] +envlist = + clean, + py{36,37,38,39} + cov +skip_missing_interpreters = True +isolated_build = True + +[testenv:clean] +skip_install: true +deps = coverage +commands = coverage erase + +[testenv] +deps = .[test] +commands = + pytest -n 0 --cov --cov-append --cov-report=term-missing --disable-warnings --junitxml=report.xml +depends = + py{36,37,38,39}: clean + cov: py{36,37,38,39} + + +[testenv:cov] +skip_install: true +deps = coverage +commands = + coverage report -m + coverage xml