Problem
When scanning a JavaScript project (.js/.jsx/.mjs files with jsconfig.json), the test coverage detector correctly classifies test files into the test zone but cannot map them to production files. This causes most modules to be reported as untested despite having comprehensive test suites.
A common example is a Next.js project created without TypeScript — these use jsconfig.json instead of tsconfig.json and .js/.jsx/.mjs files throughout.
This is a separate issue from #502 (graph path normalization sampling).
Root Cause
The JavaScript language plugin (desloppify/languages/javascript/) has no test_coverage.py module. The TypeScript plugin has one with all the required hooks:
map_test_to_source()
resolve_import_spec()
strip_test_markers()
parse_test_import_specs()
has_testable_logic()
When _load_lang_test_coverage_module("javascript") is called (in mapping_imports.py:13), it returns a bare object() via get_lang_hook(). Every subsequent getattr(mod, 'map_test_to_source', None) returns None, so both naming-based and import-based mapping silently produce empty results.
Reproduction
Public repo for testing: elfensky/helldivers.bot
- Next.js app without TypeScript, using
.mjs/.jsx/.js files with jsconfig.json (no tsconfig.json)
- Test files at
src/__tests__/unit/utils/time.test.mjs importing from @/utils/time.mjs
- 210 passing vitest tests across 16 test files
State file: state-javascript.json (language correctly auto-detected).
Result: Files like time.mjs, responses.mjs, evaluateProgress.mjs all reported as untested_module despite having dedicated test files with full coverage.
Notably, tryCatch.mjs is correctly recognized as tested — likely via direct dependency graph import edges rather than the naming/import-spec mapping pipeline.
Additional Concern
Even if the JavaScript plugin fell back to the TypeScript test_coverage.py, the _TS_EXTENSIONS list (["", ".ts", ".tsx", "/index.ts", "/index.tsx"]) would never resolve .mjs/.js/.jsx imports. A JavaScript-specific version needs extensions like ["", ".js", ".jsx", ".mjs", ".cjs", "/index.js", "/index.mjs"].
The __tests__/unit/ directory nesting pattern (e.g., src/__tests__/unit/utils/foo.test.mjs → src/utils/foo.mjs) also needs handling — the current map_test_to_source only handles one level of __tests__/ nesting.
Suggested Fix
- Create
desloppify/languages/javascript/test_coverage.py mirroring the TypeScript version with JS-appropriate extensions
- Register the
test_coverage hook in the JavaScript plugin (currently __init__.py has no register_hooks)
- Handle nested
__tests__/unit/ and __tests__/integration/ directory patterns in map_test_to_source()
- Add
.mjs/.cjs/.js/.jsx to extension resolution candidates
Environment
- desloppify 0.9.14 (pip install)
- Run via Claude Code (as a slash command / skill)
- JavaScript/Next.js project (no TypeScript) with vitest,
.mjs files, jsconfig.json
- macOS Darwin 25.4.0
Problem
When scanning a JavaScript project (
.js/.jsx/.mjsfiles withjsconfig.json), the test coverage detector correctly classifies test files into the test zone but cannot map them to production files. This causes most modules to be reported as untested despite having comprehensive test suites.A common example is a Next.js project created without TypeScript — these use
jsconfig.jsoninstead oftsconfig.jsonand.js/.jsx/.mjsfiles throughout.This is a separate issue from #502 (graph path normalization sampling).
Root Cause
The JavaScript language plugin (
desloppify/languages/javascript/) has notest_coverage.pymodule. The TypeScript plugin has one with all the required hooks:map_test_to_source()resolve_import_spec()strip_test_markers()parse_test_import_specs()has_testable_logic()When
_load_lang_test_coverage_module("javascript")is called (inmapping_imports.py:13), it returns a bareobject()viaget_lang_hook(). Every subsequentgetattr(mod, 'map_test_to_source', None)returnsNone, so both naming-based and import-based mapping silently produce empty results.Reproduction
Public repo for testing: elfensky/helldivers.bot
.mjs/.jsx/.jsfiles withjsconfig.json(notsconfig.json)src/__tests__/unit/utils/time.test.mjsimporting from@/utils/time.mjsState file:
state-javascript.json(language correctly auto-detected).Result: Files like
time.mjs,responses.mjs,evaluateProgress.mjsall reported asuntested_moduledespite having dedicated test files with full coverage.Notably,
tryCatch.mjsis correctly recognized as tested — likely via direct dependency graph import edges rather than the naming/import-spec mapping pipeline.Additional Concern
Even if the JavaScript plugin fell back to the TypeScript
test_coverage.py, the_TS_EXTENSIONSlist (["", ".ts", ".tsx", "/index.ts", "/index.tsx"]) would never resolve.mjs/.js/.jsximports. A JavaScript-specific version needs extensions like["", ".js", ".jsx", ".mjs", ".cjs", "/index.js", "/index.mjs"].The
__tests__/unit/directory nesting pattern (e.g.,src/__tests__/unit/utils/foo.test.mjs→src/utils/foo.mjs) also needs handling — the currentmap_test_to_sourceonly handles one level of__tests__/nesting.Suggested Fix
desloppify/languages/javascript/test_coverage.pymirroring the TypeScript version with JS-appropriate extensionstest_coveragehook in the JavaScript plugin (currently__init__.pyhas noregister_hooks)__tests__/unit/and__tests__/integration/directory patterns inmap_test_to_source().mjs/.cjs/.js/.jsxto extension resolution candidatesEnvironment
.mjsfiles,jsconfig.json