Skip to content

Commit 60538c0

Browse files
authored
feat: Coverage (#4)
* Added coverage; branch-level * Handling complex branching coverage * Using templates for coverage * Added report-missing * Added ty * Add JUnit XML reporter, pyproject.toml config, exclude patterns, and block coverage * Fixed build * Bump test coverage * Addressed AI reviews
1 parent 50c6c95 commit 60538c0

36 files changed

Lines changed: 6167 additions & 13 deletions

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ jobs:
1717
- uses: astral-sh/setup-uv@v7
1818
- run: uv python install ${{ matrix.python-version }}
1919
- run: uv sync --all-extras --dev
20+
- run: uv build --wheel -q # generates _templates.py via build hook
2021
- run: uv run coverage run -m pytest -q && uv run coverage report --fail-under=90
2122

2223
lint:
@@ -25,5 +26,6 @@ jobs:
2526
- uses: actions/checkout@v5
2627
- uses: astral-sh/setup-uv@v7
2728
- run: uv sync --all-extras --dev
29+
- run: uv build --wheel -q # generates _templates.py via build hook
2830
- run: uv run ruff check .
2931
- run: uv run ruff format --check .

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ dist/
66
wheels/
77
*.egg-info
88

9+
# Cache
10+
.cache
11+
912
# Virtual environments
1013
.venv
1114

@@ -18,4 +21,7 @@ uv.lock
1821
# Coverage
1922
.coverage
2023
htmlcov/
21-
*,cover
24+
*,cover
25+
26+
# Generated files
27+
jinjatest/coverage/_templates.py

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
lint:
44
uv run ruff check .
55
uv run ruff format --check .
6+
uv run ty check .
67

78
lint-fix:
89
uv run ruff check --fix .

README.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ Stop writing brittle substring assertions. Test your templates with structure, v
77
## Installation
88

99
```bash
10-
pip install jinjatest
10+
uv add jinjatest
1111
```
1212

1313
With YAML support:
1414
```bash
15-
pip install jinjatest[yaml]
15+
uv add jinjatest[yaml]
1616
```
1717

1818
## Why jinjatest?
@@ -403,6 +403,48 @@ spec = TemplateSpec.from_file("template.j2")
403403
spec.assert_variables_subset_of({"user_name", "plan", "items"})
404404
```
405405

406+
## Template Coverage
407+
408+
Track branch coverage for your Jinja templates to ensure all conditional paths are tested.
409+
410+
### Quick Start
411+
412+
```bash
413+
pytest --jt-cov
414+
```
415+
416+
### CLI Options
417+
418+
| Option | Description |
419+
|--------|-------------|
420+
| `--jt-cov` | Enable template coverage |
421+
| `--jt-cov-fail-under=N` | Fail if coverage below N% |
422+
| `--jt-cov-report=TYPE` | Report type: `term`, `term-missing`, `term-verbose`, `html`, `json`, `xml` |
423+
| `--jt-cov-html=DIR` | HTML report directory |
424+
| `--jt-cov-json=FILE` | JSON report file |
425+
| `--jt-cov-xml=FILE` | JUnit XML report file |
426+
| `--jt-cov-exclude=PATTERN` | Glob pattern to exclude templates |
427+
428+
### Configuration (pyproject.toml)
429+
430+
```toml
431+
[tool.jinjatest.coverage]
432+
enabled = true
433+
fail_under = 80
434+
report = ["term", "html"]
435+
html_dir = "jt-htmlcov"
436+
exclude_patterns = ["**/vendor/**", "*.partial.j2"]
437+
```
438+
439+
### What's Tracked
440+
441+
- `{% if %}` / `{% elif %}` / `{% else %}` branches
442+
- `{% for %}` loops (body and else)
443+
- `{% macro %}` definitions
444+
- `{% block %}` definitions (template inheritance)
445+
- `{% include %}` statements
446+
- Ternary expressions (`{{ x if cond else y }}`)
447+
406448
## License
407449

408450
MIT

jinjatest/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def test_welcome_pro_user():
9797
"TemplateMarkers",
9898
# Utilities
9999
"normalize_text",
100+
# Coverage (lazy import via jinjatest.coverage)
100101
]
101102

102103
# Optional YAML exports

jinjatest/coverage/__init__.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Jinja template branch coverage tracking.
3+
4+
This module provides automatic branch coverage tracking for Jinja templates.
5+
6+
Example:
7+
from jinjatest.coverage import (
8+
get_coverage_collector,
9+
CoverageReporter,
10+
ReportConfig,
11+
)
12+
13+
# Enable coverage collection
14+
collector = get_coverage_collector()
15+
collector.enable()
16+
17+
# ... run tests with TemplateSpec ...
18+
19+
# Generate reports
20+
summary = collector.get_summary()
21+
reporter = CoverageReporter(ReportConfig(fail_under=80))
22+
reporter.terminal_report(summary)
23+
"""
24+
25+
from jinjatest.coverage.collector import (
26+
CoverageCollector,
27+
CoverageSummary,
28+
get_coverage_collector,
29+
reset_coverage_collector,
30+
set_coverage_collector,
31+
)
32+
from jinjatest.coverage.discovery import (
33+
BranchDiscovery,
34+
BranchInfo,
35+
DiscoveryResult,
36+
)
37+
from jinjatest.coverage.instrumenter import (
38+
AutoInstrumenter,
39+
InstrumentationResult,
40+
)
41+
from jinjatest.coverage.reporter import (
42+
CoverageReporter,
43+
HTMLReporter,
44+
JSONReporter,
45+
JUnitReporter,
46+
ReportConfig,
47+
TerminalReporter,
48+
)
49+
from jinjatest.coverage.tracker import (
50+
BranchCoverage,
51+
TemplateCoverage,
52+
TemplateCoverageStats,
53+
)
54+
55+
__all__ = [
56+
"CoverageCollector",
57+
"CoverageSummary",
58+
"get_coverage_collector",
59+
"set_coverage_collector",
60+
"reset_coverage_collector",
61+
"BranchDiscovery",
62+
"BranchInfo",
63+
"DiscoveryResult",
64+
"AutoInstrumenter",
65+
"InstrumentationResult",
66+
"TemplateCoverage",
67+
"TemplateCoverageStats",
68+
"BranchCoverage",
69+
"CoverageReporter",
70+
"TerminalReporter",
71+
"JSONReporter",
72+
"HTMLReporter",
73+
"JUnitReporter",
74+
"ReportConfig",
75+
]

0 commit comments

Comments
 (0)