Skip to content

fix(js): add test coverage detection for JavaScript modules#29

Merged
samtuckerdavis merged 3 commits intomainfrom
fix/js-test-coverage-detection
Apr 18, 2026
Merged

fix(js): add test coverage detection for JavaScript modules#29
samtuckerdavis merged 3 commits intomainfrom
fix/js-test-coverage-detection

Conversation

@samtuckerdavis
Copy link
Copy Markdown

@samtuckerdavis samtuckerdavis commented Apr 18, 2026

Summary

  • Adds test_coverage.py to the JavaScript language plugin with naming convention mapping, import spec parsing (ESM and CommonJS), and test quality patterns
  • Fixes a path resolution bug in ts_build_dep_graph where project-root-relative paths from resolve_js_import were double-joined with the scan path prefix
  • Registers the new hooks via generic_lang test_coverage_module parameter

Problem

JavaScript projects using .test.js naming convention showed 0% test health regardless of test coverage. Two root causes:

  1. The JavaScript plugin had no test_coverage.py, so naming_based_mapping (which strips .test. from filenames to find production files) returned empty for lang_name='javascript'
  2. ts_build_dep_graph had a path mismatch: resolve_js_import returns project-root-relative paths (e.g. _ui/server.js) but the graph builder joined them with the already-prefixed scan path (/path/to/_ui/), producing invalid absolute paths (/path/to/_ui/_ui/server.js) that never matched the file set

Test plan

  • Run desloppify scan on a JavaScript project with .test.js files — should show > 0% test health
  • Verify naming_based_mapping('javascript') maps foo.test.jsfoo.js
  • Verify import graph correctly detects import ... from './module.js' relationships
  • Verify CommonJS require('./module') is also parsed by parse_test_import_specs

Summary by CodeRabbit

  • New Features

    • JavaScript test coverage detection added with intelligent test-to-source mapping, assertion/mock/test detection, and support for common filename markers and extensions.
  • Improvements

    • Import resolution now correctly handles project-root-relative dependency paths.
  • Tests

    • New unit tests validating project-root-relative import handling and JavaScript test coverage heuristics.

- Add test_coverage.py to JavaScript language plugin with naming
  convention mapping (strip_test_markers, map_test_to_source),
  import spec parsing for ESM and CommonJS require(), and quality
  detection patterns (ASSERT_PATTERNS, TEST_FUNCTION_RE)
- Register test_coverage hooks via generic_lang test_coverage_module
  parameter in javascript/__init__.py
- Fix path resolution bug in ts_build_dep_graph where resolve_import
  returning a project-root-relative path was incorrectly joined with
  the scan path (already containing the subdir prefix), causing all
  imports to resolve outside the file_set

Without these fixes, JavaScript projects using .test.js naming
convention showed 0% test health regardless of test coverage.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Warning

Rate limit exceeded

@stuckvgn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 42 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 42 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6b60ec2c-2393-4017-8732-66d37f7b7193

📥 Commits

Reviewing files that changed from the base of the PR and between 0faf9fa and 20f0afd.

📒 Files selected for processing (1)
  • desloppify/tests/lang/javascript/test_javascript_test_coverage.py
📝 Walkthrough

Walkthrough

Refines Treesitter import dependency resolution to accept project-root-relative resolver outputs without forced normalization. Adds a JavaScript test-coverage module with import extraction, test-to-source mapping, and import-spec resolution, and wires it into the JavaScript language plugin.

Changes

Cohort / File(s) Summary
Import Resolution Enhancement
desloppify/languages/_framework/treesitter/imports/graph.py
Detects when a resolver returns a non-absolute path that already exists in file_set and records the import edge immediately, skipping join/normalization and later within-scan filtering.
JavaScript Test Coverage Integration
desloppify/languages/javascript/__init__.py, desloppify/languages/javascript/test_coverage.py
Adds a JS test-coverage module providing parse_test_import_specs(), map_test_to_source(), strip_test_markers(), and resolve_import_spec(). Integrates the module via test_coverage_module in the JS language config.
Tests
desloppify/tests/lang/common/test_treesitter_imports_direct.py, desloppify/tests/lang/javascript/test_javascript_test_coverage.py
Adds tests for project-root-relative import handling in the Treesitter graph and unit tests for the new JS test-coverage heuristics and resolution logic.

Sequence Diagram(s)

sequenceDiagram
    participant TestFile as Test File
    participant Coverage as JS Test Coverage Module
    participant FS as Production Files (set)
    participant Graph as Import Graph
    Note over TestFile,Coverage: parse_test_import_specs(content)
    TestFile->>Coverage: provide file content
    Coverage->>Coverage: extract import specs (ESM/CJS/dynamic)
    Coverage->>FS: resolve relative specs (resolve_import_spec)
    FS-->>Coverage: matched production path or None
    Coverage->>Coverage: map_test_to_source(test_path, production_set)
    Coverage-->>TestFile: mapped production path / None
    Note over Coverage,Graph: graph import edges updated elsewhere using resolved paths
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding test coverage detection functionality for JavaScript modules, which aligns with the core feature introduced across the changeset.
No Hardcoded Secrets Or Credentials ✅ Passed Non-test files contain no hardcoded API keys, tokens, passwords, or credentials; only standard configuration and public framework patterns.
No Speciesist Idioms ✅ Passed All five modified files verified for absence of speciesist idioms including 'livestock', 'master/slave', 'whitelist/blacklist', 'cattle', 'kill two birds', 'guinea pig', and 'farm'. Pull request adheres to inclusive language standards.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/js-test-coverage-detection
  • 🛠️ fix NAV violations: Commit on current branch
  • 🛠️ fix NAV violations: Create PR

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 18, 2026

Auto-Merge Gate Check — Blocked

PR labeled needs-human-review — one or more gates failed.

Failed Gates

  • Classification label missing — PR needs wave0-auto or level-0 label
  • PR author 'stuckvgn' is not a known agent bot
  • CI checks not all green: Incomplete: check, arch-contracts, tests-full, package-smoke, ci-contracts, lint, typecheck, tests-core.

How to unblock

  • Fix the failing gates and push a new commit — this workflow re-runs automatically.
  • If CI is still running, wait for it to complete.
  • If NAV config is absent, open a task to install the no-animal-violence suite.

Permanent block

  • Add the human-gate label to permanently disable auto-merge for this PR.

Auto-Merge Gate Check — 2026-04-18T16:06:04.175Z

coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 18, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@desloppify/languages/_framework/treesitter/imports/graph.py`:
- Around line 78-87: Add a characterization test that reproduces the bug where
resolve_import returns a project-root-relative path and the code incorrectly
joins it with scan_path: in the test, stub/mock resolve_import to return a
project-root-relative path that is present in file_set, set scan_path different
from project root so the pre-fix code would join it to produce a wrong absolute
path, then assert that the graph built by the module (the function that
populates graph and uses file_set, resolve_import, and scan_path) contains the
incorrect import path prior to applying the fix and that after the fix the
import recorded is the original project-root-relative path (i.e., no scan_path
join). Use identifiers file_set, resolve_import, scan_path, and the
graph-building function from imports/graph.py to locate and hook behavior.

In `@desloppify/languages/javascript/test_coverage.py`:
- Around line 50-57: Change parse_test_import_specs to return a set[str] instead
of list[str]: update the function signature and build/return a set of specs (use
a set comprehension or add specs into a set while iterating over
_IMPORT_RE.finditer(content)) so duplicates are removed and semantics match the
Python/TypeScript hooks; keep the extraction logic using m.group(1) or
m.group(2) but insert each non-empty spec into the set before returning it.
- Around line 79-87: The current match logic can return wrong files on basename
collisions because it iterates production_set first nondeterministically; change
the logic to first check for exact path matches by iterating candidates and
returning any candidate present in production_set (use the existing check `if c
in production_set: return c`), and only if no exact match is found, build a
deterministic basename fallback: map basenames (os.path.basename) from
production_set to a sorted list of full paths and then for each candidate check
its basename against that map and return the first production path from the
sorted list; update the code around variables production_set, candidates,
prod_base, and os.path.basename to implement this order and deterministic
selection.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d1eb4c24-c6a9-4891-93ce-fd1364d71605

📥 Commits

Reviewing files that changed from the base of the PR and between 4aa174f and 805d9af.

📒 Files selected for processing (3)
  • desloppify/languages/_framework/treesitter/imports/graph.py
  • desloppify/languages/javascript/__init__.py
  • desloppify/languages/javascript/test_coverage.py

Comment thread desloppify/languages/_framework/treesitter/imports/graph.py
Comment thread desloppify/languages/javascript/test_coverage.py Outdated
Comment thread desloppify/languages/javascript/test_coverage.py Outdated
@samtuckerdavis samtuckerdavis enabled auto-merge (squash) April 18, 2026 15:42
…asename, characterization test

- Change parse_test_import_specs return type from list[str] to set[str] to deduplicate
  specs and match Python/TypeScript hook semantics
- Make map_test_to_source deterministic on basename collisions: exact path match first,
  then sorted basename fallback using a stable basename-to-path mapping
- Add characterization test for the graph.py project-root-relative path resolution bug:
  verifies that resolve_import returning a relative path in file_set is recorded directly
  without being incorrectly joined with scan_path
- Add tests/lang/javascript/ with full coverage of JavaScript test_coverage.py hooks
@samtuckerdavis
Copy link
Copy Markdown
Author

Addressed all three CodeRabbit review items in the latest commit:

  1. parse_test_import_specs return type — changed from list[str] to set[str] using a set comprehension, eliminating duplicates and matching Python/TypeScript hook semantics.

  2. Deterministic basename fallback in map_test_to_source — rewrote the fallback to: (a) try exact path matches first, then (b) build a sorted basename → [paths] map and iterate sorted candidates, returning the first match from the sorted path list. This removes set-iteration non-determinism on basename collisions.

  3. Characterization test for graph.py path resolution bug — added test_graph_records_project_root_relative_import_without_scan_path_join to test_treesitter_imports_direct.py. The test stubs resolve_import to return a project-root-relative path present in file_set, verifies the edge is recorded correctly (the pre-fix code would have joined with scan_path producing the wrong double-nested path).

Also added tests/lang/javascript/ with 10 tests covering all exported hooks in test_coverage.py.

All 6643 tests pass locally.

coderabbitai[bot]
coderabbitai Bot previously requested changes Apr 18, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@desloppify/tests/lang/javascript/test_javascript_test_coverage.py`:
- Around line 10-22: The test test_parse_test_import_specs_handles_esm_and_cjs
is missing an assertion for the side-effect import; update the test in
desloppify/tests/lang/javascript/test_javascript_test_coverage.py to assert that
js_cov.parse_test_import_specs(content) includes "./side-effect.js" (i.e., add
an assertion like assert "./side-effect.js" in specs) so that
parse_test_import_specs is validated for side-effect import extraction alongside
"./foo.js", "./bar", and "./lazy.js".
- Around line 70-77: The test
test_map_test_to_source_is_deterministic_on_basename_collision currently only
asserts stability but not the actual deterministic choice; update it to assert
the concrete expected path returned by js_cov.map_test_to_source for the input
production = {"a/util.js","b/util.js"} (i.e., assert result1 == "a/util.js") in
addition to the repeated-call stability check so the test fails if the intended
sorted-choice fallback changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a410f671-23d8-43a3-a3e1-84b66ad29531

📥 Commits

Reviewing files that changed from the base of the PR and between 805d9af and 0faf9fa.

📒 Files selected for processing (4)
  • desloppify/languages/javascript/test_coverage.py
  • desloppify/tests/lang/common/test_treesitter_imports_direct.py
  • desloppify/tests/lang/javascript/__init__.py
  • desloppify/tests/lang/javascript/test_javascript_test_coverage.py

Comment thread desloppify/tests/lang/javascript/test_javascript_test_coverage.py
Comment thread desloppify/tests/lang/javascript/test_javascript_test_coverage.py Outdated
@samtuckerdavis samtuckerdavis dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] April 18, 2026 16:03

All three requested changes addressed in latest commit: set return type for parse_test_import_specs, deterministic basename fallback in map_test_to_source, and characterization test for graph.py path resolution bug.

…ssertion and concrete deterministic value check

- Assert './side-effect.js' is extracted by parse_test_import_specs (side-effect import coverage)
- Assert map_test_to_source returns 'a/util.js' (lexicographically first) on basename collision,
  not just stability — pins the intended sorted-choice fallback behavior
@samtuckerdavis samtuckerdavis merged commit 0b0b10d into main Apr 18, 2026
11 checks passed
@samtuckerdavis samtuckerdavis deleted the fix/js-test-coverage-detection branch April 22, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant