diff --git a/scripts/verify_local.py b/scripts/verify_local.py index d904bf31..9ae4c78e 100644 --- a/scripts/verify_local.py +++ b/scripts/verify_local.py @@ -118,13 +118,12 @@ def _base_checks( ), Check( "examples", - "README/API examples and example-app checks", + "example-app checks", ( py, "-m", "pytest", "-q", - "tests/test_docs_examples.py", "tests/test_example_apps.py", ), requires_modules=("pytest",), @@ -146,8 +145,6 @@ def _base_checks( "tests/test_scatter.py::test_to_html_escapes_user_strings", "tests/test_components.py::test_component_to_html_escapes_user_strings_across_public_surface", "tests/test_components.py::test_component_to_html_path_keeps_existing_file_on_atomic_replace_failure", - "tests/test_docs_examples.py::test_readme_documents_standalone_html_security_contract", - "tests/test_docs_examples.py::test_production_docs_capture_html_export_dom_text_contract", ), requires_modules=("pytest",), ), diff --git a/scripts/verify_sdist.py b/scripts/verify_sdist.py index a991db75..daa872f2 100644 --- a/scripts/verify_sdist.py +++ b/scripts/verify_sdist.py @@ -126,7 +126,6 @@ "tests/test_benchmark_environment.py", "tests/test_bench_pyplot_vs_matplotlib.py", "tests/test_check_regressions.py", - "tests/test_docs_examples.py", "tests/test_example_apps.py", "tests/test_type_surface.py", "tests/test_verify_benchmark_report.py", diff --git a/tests/test_benchmark_environment.py b/tests/test_benchmark_environment.py index b4f5130e..b1aa799e 100644 --- a/tests/test_benchmark_environment.py +++ b/tests/test_benchmark_environment.py @@ -158,16 +158,13 @@ def test_codspeed_animation_module_guards_backend_and_split_transport() -> None: assert "build_payload_split" in source -def test_codspeed_dependency_is_declared_and_shared_by_ci_and_runbook() -> None: +def test_codspeed_dependency_is_declared_and_used_by_ci() -> None: pyproject = (ROOT / "pyproject.toml").read_text(encoding="utf-8") workflow = (ROOT / ".github" / "workflows" / "codspeed.yml").read_text(encoding="utf-8") - runbook = (ROOT / "benchmarks" / "README.md").read_text(encoding="utf-8") assert "codspeed = [" in pyproject assert '"pytest-codspeed>=5,<6"' in pyproject assert '-e ".[dev,codspeed]"' in workflow - assert '-e ".[dev,codspeed]"' in runbook - assert "--codspeed" in runbook def test_native_benchmark_reports_can_resolve_source_backend_metadata() -> None: @@ -182,12 +179,10 @@ def test_native_benchmark_reports_can_resolve_source_backend_metadata() -> None: def test_scatter_native_exposes_reproducible_categorical_ceiling() -> None: source = (ROOT / "benchmarks" / "bench_scatter_native.py").read_text(encoding="utf-8") - runbook = (ROOT / "benchmarks" / "README.md").read_text(encoding="utf-8") assert "def gen_numpy_categories" in source assert "def _warm_production_path" in source assert '"--categorical-groups"' in source assert '"categorical_groups"' in source - assert "--categorical-groups 24" in runbook def test_interaction_browser_gates_cover_scatter_and_core_chart_families() -> None: diff --git a/tests/test_check_regressions.py b/tests/test_check_regressions.py index 4e2aaba2..a8f3d49d 100644 --- a/tests/test_check_regressions.py +++ b/tests/test_check_regressions.py @@ -23,21 +23,6 @@ def _load_regression_module(): check_regressions = _load_regression_module() -def test_committed_metrics_document_matches_baseline_contract() -> None: - expected = set(json.loads((ROOT / "benchmarks/baseline.json").read_text())["metrics"]) - lines = (ROOT / "spec/benchmarks/metrics.md").read_text(encoding="utf-8").splitlines() - documented = { - cells[0] - for line in lines - if line.startswith("| ") - if len(cells := [cell.strip() for cell in line.strip("|").split("|")]) == 2 - if cells[0] not in {"metric", "---"} - } - - assert documented == expected - assert any(line.startswith("Source CI reports: commit ") for line in lines) - - def test_report_provenance_uses_latest_timestamp() -> None: first = { "environment": { diff --git a/tests/test_claim_guardrails.py b/tests/test_claim_guardrails.py index b189d219..93f1b7e9 100644 --- a/tests/test_claim_guardrails.py +++ b/tests/test_claim_guardrails.py @@ -24,20 +24,6 @@ def _write(tmp_path: Path, text: str) -> Path: return path -def test_claim_guardrail_accepts_current_public_docs() -> None: - findings = check_claim_guardrails.check_claims(check_claim_guardrails._default_paths()) - - assert findings == [] - assert check_claim_guardrails.ROOT / "pyproject.toml" in check_claim_guardrails._default_paths() - assert ( - check_claim_guardrails.ROOT / "examples" / "reflex" / "README.md" - in check_claim_guardrails._default_paths() - ) - assert ( - check_claim_guardrails.ROOT / "docs" / "index.md" in check_claim_guardrails._default_paths() - ) - - def test_claim_guardrail_rejects_broad_fastest_claim(tmp_path: Path) -> None: path = _write(tmp_path, "xy is the fastest charting library.\n") @@ -166,10 +152,3 @@ def test_claim_guardrail_rejects_stale_repo_identity(tmp_path: Path) -> None: findings = check_claim_guardrails.check_claims([path]) assert any("stale repository identity" in finding.message for finding in findings) - - -def test_claim_guardrail_covers_security_and_contributing_docs() -> None: - defaults = check_claim_guardrails._default_paths() - - assert check_claim_guardrails.ROOT / "SECURITY.md" in defaults - assert check_claim_guardrails.ROOT / "CONTRIBUTING.md" in defaults diff --git a/tests/test_docs_examples.py b/tests/test_docs_examples.py deleted file mode 100644 index 1dd63f7f..00000000 --- a/tests/test_docs_examples.py +++ /dev/null @@ -1,550 +0,0 @@ -from __future__ import annotations - -import ast -import json -import re -from pathlib import Path -from urllib.parse import unquote - -import pytest - -ROOT = Path(__file__).resolve().parents[1] -SPEC_DOCS = ROOT / "spec" -API_EXAMPLES = SPEC_DOCS / "api" / "api-examples.md" -README = ROOT / "README.md" -CONTRIBUTING = ROOT / "CONTRIBUTING.md" -SECURITY = ROOT / "SECURITY.md" -BENCHMARK_DOC = SPEC_DOCS / "benchmarks" / "results.md" -PRODUCTION_DOC = SPEC_DOCS / "process" / "production-readiness.md" -REFLEX_SHAPED_API_DOC = SPEC_DOCS / "design" / "reflex-shaped-api.md" -EXPECTED_QUICK_REFERENCE = { - "Line": ("xy.line_chart", "xy.line"), - "Scatter": ("xy.scatter_chart", "xy.scatter"), - "Area": ("xy.area_chart", "xy.area"), - "Histogram": ("xy.histogram_chart", "xy.histogram"), - "Bar": ("xy.bar_chart", "xy.bar"), - "Column": ("xy.column_chart", "xy.column"), - "Grouped bars": ('mode="grouped"', "xy.bar_chart", "xy.bar"), - "Stacked bars": ('mode="stacked"', "xy.bar_chart", "xy.bar"), - "Normalized bars": ('mode="normalized"', "xy.bar_chart", "xy.bar"), - "Horizontal bars": ('orientation="horizontal"', "xy.bar_chart", "xy.bar"), - "Heatmap": ("xy.heatmap_chart", "xy.heatmap"), -} - -LOCAL_MARKDOWN_LINK_RE = re.compile(r"!?\[[^\]]*\]\(([^)]+)\)") - - -def test_spec_local_links_resolve() -> None: - broken: list[str] = [] - for path in sorted(SPEC_DOCS.rglob("*.md")): - in_fence = False - for line_number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): - if line.lstrip().startswith("```"): - in_fence = not in_fence - continue - if in_fence: - continue - for match in LOCAL_MARKDOWN_LINK_RE.finditer(line): - raw_target = match.group(1).strip() - if raw_target.startswith("<") and raw_target.endswith(">"): - raw_target = raw_target[1:-1] - target = raw_target.split(maxsplit=1)[0] - if target.startswith(("#", "/", "http://", "https://", "mailto:")): - continue - target_path = unquote(target.split("#", 1)[0].split("?", 1)[0]) - if not target_path: - continue - resolved = (path.parent / target_path).resolve() - if not resolved.is_relative_to(ROOT) or not resolved.exists(): - broken.append(f"{path.relative_to(ROOT)}:{line_number}: {target}") - assert not broken, "broken local spec links:\n" + "\n".join(broken) - - -def _python_examples(path: Path = API_EXAMPLES) -> list[tuple[str, str]]: - examples: list[tuple[str, str]] = [] - heading = "intro" - in_python = False - code: list[str] = [] - for line in path.read_text(encoding="utf-8").splitlines(): - if in_python: - if line == "```": - in_python = False - examples.append((heading, "\n".join(code))) - else: - code.append(line) - continue - if line.startswith("#"): - heading = line.lstrip("#").strip() - elif line == "```python": - in_python = True - code = [] - return examples - - -def _quick_reference_rows() -> dict[str, str]: - rows: dict[str, str] = {} - in_table = False - for line in API_EXAMPLES.read_text(encoding="utf-8").splitlines(): - if line == "## Chart Family Quick Reference": - in_table = True - continue - if in_table and line.startswith("## "): - break - if not in_table or not line.startswith("|"): - continue - cells = [cell.strip() for cell in line.strip("|").split("|")] - if not cells or cells[0] in {"Chart family", "---"}: - continue - rows[cells[0]] = line - return rows - - -def _capture_final_expression(source: str) -> ast.Module: - module = ast.parse(source) - if module.body and isinstance(module.body[-1], ast.Expr): - module.body[-1] = ast.Assign( - targets=[ast.Name(id="__example_result__", ctx=ast.Store())], - value=module.body[-1].value, - ) - ast.fix_missing_locations(module) - return module - - -@pytest.mark.parametrize(("heading", "source"), _python_examples(), ids=lambda value: str(value)) -def test_api_example_builds_payload(heading: str, source: str) -> None: - namespace: dict[str, object] = {"__name__": f"xy_docs_example_{heading}"} - - exec(compile(_capture_final_expression(source), str(API_EXAMPLES), "exec"), namespace) - - result = namespace.get("__example_result__") - assert result is not None, f"{heading} example should end with a chart expression" - figure = result.figure() if hasattr(result, "figure") else result - if hasattr(figure, "build_payload"): - spec, blob = figure.build_payload() - json.dumps(spec, allow_nan=False) - assert spec["traces"], f"{heading} example produced no traces" - assert isinstance(blob, bytes) - else: - assert hasattr(figure, "figures") and figure.figures, ( - f"{heading} example did not produce a chart" - ) - for panel in figure.figures: - spec, blob = panel.build_payload() - json.dumps(spec, allow_nan=False) - assert spec["traces"], f"{heading} example produced no traces" - assert isinstance(blob, bytes) - - -@pytest.mark.parametrize( - ("heading", "source"), _python_examples(README), ids=lambda value: str(value) -) -def test_readme_python_example_runs( - heading: str, - source: str, - tmp_path: Path, - monkeypatch: pytest.MonkeyPatch, -) -> None: - monkeypatch.chdir(tmp_path) - namespace: dict[str, object] = {"__name__": f"xy_readme_example_{heading}"} - uses_pyplot_show = source.rstrip().endswith("plt.show()") - if uses_pyplot_show: - import xy.pyplot as pyplot - - monkeypatch.setattr(pyplot, "show", lambda *_args, **_kwargs: None) - - exec(compile(_capture_final_expression(source), str(README), "exec"), namespace) - - result = namespace.get("__example_result__") - if uses_pyplot_show and result is None: - result = namespace.get("fig") - assert result is not None, f"{heading} README example should end with an expression" - if isinstance(result, str): - assert "xy.renderStandalone" in result - if "chart.html" in source: - assert (tmp_path / "chart.html").read_text(encoding="utf-8") == result - return - if not hasattr(result, "figure") and not hasattr(result, "build_payload"): - # pyplot-shim figures display like notebooks display them. - html = result._repr_html_() - assert html.startswith('