f3d80b4628
Auto Release / auto-release (push) Failing after 1s
CI / lint (push) Failing after 0s
CI / screenshot-quality (push) Failing after 3s
CI / pr-policy (push) Has been skipped
CI / test (3.11) (push) Failing after 0s
CI / test (3.12) (push) Failing after 0s
CI / test (3.13) (push) Failing after 3s
CI / coverage (push) Failing after 1s
Legibility / legibility (push) Failing after 3s
210 lines
6.6 KiB
Python
210 lines
6.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
|
|
import scripts.check_coverage as coverage_module
|
|
from scripts.check_coverage import (
|
|
VIEWER_JS_SOURCES,
|
|
_filter_pure_viewer_asset_split,
|
|
_is_function_covered,
|
|
_tag_content,
|
|
_viewer_script_functions,
|
|
changed_lines_from_diff,
|
|
changed_viewer_css_selectors,
|
|
changed_viewer_functions,
|
|
check_python_coverage,
|
|
check_viewer_css_coverage,
|
|
css_selector_ranges,
|
|
js_function_ranges,
|
|
)
|
|
|
|
|
|
def test_changed_lines_from_diff_extracts_new_line_numbers() -> None:
|
|
diff = """diff --git a/claude_tap/viewer.py b/claude_tap/viewer.py
|
|
--- a/claude_tap/viewer.py
|
|
+++ b/claude_tap/viewer.py
|
|
@@ -10,0 +11,2 @@
|
|
+def added():
|
|
+ return True
|
|
@@ -20,2 +22,2 @@
|
|
-old = 1
|
|
+new = 2
|
|
context = True
|
|
"""
|
|
|
|
assert changed_lines_from_diff(diff) == {"claude_tap/viewer.py": {11, 12, 22}}
|
|
|
|
|
|
def test_filter_pure_viewer_asset_split_ignores_exact_extracted_assets(monkeypatch, tmp_path) -> None:
|
|
base_html = """<html><head><style>
|
|
.x { color: red; }
|
|
</style></head><body><script>
|
|
function moved() { return true; }
|
|
</script></body></html>
|
|
"""
|
|
assets = tmp_path / "claude_tap" / "viewer_assets"
|
|
assets.mkdir(parents=True)
|
|
(assets / "viewer.css").write_text(".x { color: red; }\n", encoding="utf-8")
|
|
for source in VIEWER_JS_SOURCES:
|
|
path = tmp_path / source
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text("", encoding="utf-8")
|
|
(tmp_path / VIEWER_JS_SOURCES[0]).write_text("function moved() { return true; }\n", encoding="utf-8")
|
|
(tmp_path / "claude_tap" / "viewer.html").write_text(
|
|
"""<html><head><!-- CLAUDE_TAP_VIEWER_STYLE --></head><body><!-- CLAUDE_TAP_VIEWER_SCRIPT --></body></html>
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
def fake_check_output(cmd, **kwargs):
|
|
assert cmd == ["git", "show", "origin/main:claude_tap/viewer.html"]
|
|
return base_html
|
|
|
|
monkeypatch.setattr(coverage_module, "REPO_ROOT", tmp_path)
|
|
monkeypatch.setattr(subprocess, "check_output", fake_check_output)
|
|
|
|
filtered = _filter_pure_viewer_asset_split(
|
|
{
|
|
"claude_tap/viewer_assets/viewer.css": {1},
|
|
VIEWER_JS_SOURCES[0]: {1},
|
|
"claude_tap/viewer.html": {1},
|
|
"claude_tap/viewer.py": {10},
|
|
},
|
|
"origin/main",
|
|
)
|
|
|
|
assert filtered == {"claude_tap/viewer.py": {10}}
|
|
assert _tag_content(base_html, "style") == ".x { color: red; }"
|
|
|
|
|
|
def test_changed_viewer_functions_does_not_fallback_to_template_lines_for_split_asset(tmp_path) -> None:
|
|
viewer = tmp_path / "state.js"
|
|
viewer.write_text(
|
|
"""function realAssetFunction() {
|
|
return true;
|
|
}
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
assert changed_viewer_functions(viewer, {"claude_tap/viewer.html": {1, 2}}) == set()
|
|
|
|
|
|
def test_check_python_coverage_counts_only_changed_executable_package_lines(tmp_path) -> None:
|
|
report = {
|
|
"totals": {"percent_covered": 75.0},
|
|
"files": {
|
|
"claude_tap/viewer.py": {
|
|
"executed_lines": [10, 11, 13],
|
|
"missing_lines": [12],
|
|
"excluded_lines": [],
|
|
}
|
|
},
|
|
}
|
|
report_path = tmp_path / "coverage.json"
|
|
report_path.write_text(json.dumps(report), encoding="utf-8")
|
|
|
|
results = check_python_coverage(
|
|
report_path,
|
|
{"claude_tap/viewer.py": {10, 12, 99}, "tests/test_viewer.py": {1}},
|
|
total_min=65.0,
|
|
diff_min=80.0,
|
|
)
|
|
|
|
assert results[0].name == "python_total"
|
|
assert results[0].passed is True
|
|
assert results[1].name == "python_diff"
|
|
assert results[1].percent == 50.0
|
|
assert results[1].passed is False
|
|
assert results[1].detail == "1/2 changed executable Python lines covered"
|
|
|
|
|
|
def test_js_function_ranges_and_changed_viewer_functions_find_touched_functions(tmp_path) -> None:
|
|
viewer = tmp_path / "viewer.js"
|
|
viewer.write_text(
|
|
"""function untouched() {
|
|
return 1;
|
|
}
|
|
function changedOne() {
|
|
const value = 2;
|
|
return value;
|
|
}
|
|
function changedTwo() { return 3; }
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
assert js_function_ranges(viewer.read_text(encoding="utf-8")) == {
|
|
"untouched": (1, 3),
|
|
"changedOne": (4, 7),
|
|
"changedTwo": (8, 8),
|
|
}
|
|
assert changed_viewer_functions(
|
|
viewer,
|
|
{"claude_tap/viewer_assets/viewer.js": {5, 8}},
|
|
) == {"changedOne", "changedTwo"}
|
|
|
|
|
|
def test_css_selector_ranges_and_changed_viewer_css_selectors_find_touched_rules(tmp_path) -> None:
|
|
viewer = tmp_path / "viewer.css"
|
|
viewer.write_text(
|
|
""".header, .toolbar {
|
|
display: flex;
|
|
}
|
|
.button:hover { color: blue; }
|
|
@media (max-width: 768px) {
|
|
#detail.mobile-fullwidth { width: 100%; }
|
|
.header { display: block; }
|
|
}
|
|
""",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
assert css_selector_ranges(viewer.read_text(encoding="utf-8")) == {
|
|
".header": [(1, 3), (7, 7)],
|
|
".toolbar": [(1, 3)],
|
|
"#detail.mobile-fullwidth": [(6, 6)],
|
|
}
|
|
assert changed_viewer_css_selectors(
|
|
viewer,
|
|
{"claude_tap/viewer_assets/viewer.css": {2, 6, 7}},
|
|
) == {".header", ".toolbar", "#detail.mobile-fullwidth"}
|
|
assert changed_viewer_css_selectors(
|
|
viewer,
|
|
{"claude_tap/viewer_assets/viewer.css": {7}},
|
|
) == {".header"}
|
|
|
|
|
|
def test_viewer_script_functions_filters_top_level_wrapper_and_detects_coverage() -> None:
|
|
script = {
|
|
"functions": [
|
|
{"functionName": "", "ranges": [{"startOffset": 0, "endOffset": 1000, "count": 1}]},
|
|
{"functionName": "renderEmptyTraceState", "ranges": [{"startOffset": 100, "endOffset": 220, "count": 1}]},
|
|
{"functionName": "initFileDropZone", "ranges": [{"startOffset": 240, "endOffset": 320, "count": 0}]},
|
|
]
|
|
}
|
|
|
|
functions = _viewer_script_functions(script)
|
|
|
|
assert [function["functionName"] for function in functions] == ["renderEmptyTraceState", "initFileDropZone"]
|
|
assert _is_function_covered(functions[0]) is True
|
|
assert _is_function_covered(functions[1]) is False
|
|
|
|
|
|
def test_check_viewer_css_coverage_enforces_changed_selector_matches() -> None:
|
|
results = check_viewer_css_coverage(
|
|
{".covered", ".missing"},
|
|
selector_min=60.0,
|
|
diff_min=80.0,
|
|
coverage=(75.0, {".covered", ".other"}, 3, 4, 1),
|
|
)
|
|
|
|
assert results[0].name == "viewer_css_selectors"
|
|
assert results[0].passed is True
|
|
assert results[0].detail == "3/4 queryable CSS selectors matched; 1 state/pseudo selectors skipped"
|
|
assert results[1].name == "viewer_css_diff"
|
|
assert results[1].percent == 50.0
|
|
assert results[1].passed is False
|
|
assert results[1].detail == "1/2 changed CSS selectors matched; missing: .missing"
|