Closed bvallee-thefork closed 2 years ago
@geek got time to look into this one?
This is not going to be fixed. Going to drop sourcemap support.
@hueniverse Is there a reason sourcemap support is being dropped?
I've started diving into the @hapijs/lab source code to figure out what it would realistically take to fix the issue of incorrect line counts and the "missing coverage from file(s): null on line(s): , , , ," issue
The primary issue is that the compiled TypeScript files have boilerplate code being added which is not covered by any unit tests. Things like:
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
First I noticed the "missing coverage from" messaging was in the console reporter (lib/reporters/console.js) and found I could pretty trivially hide the offending lines by modifying the if (file.sourcemaps) {
block -- in particular, I changed if (line.miss) {
to if (line.originalFilename && line.miss) {
-- this way misses would only be displayed in the output if they mapped to a line in the original source code, and misses that were added by any compilers or transformations would be ignored. Of course there's still the matter of correcting the reported percentage and the total misses count, but this was part of my initial exploration
Since we want the underlying coverage data to be correct and not just how it's output in the console (e.g. if you output to HTML or JSON, those should be correct as well) I started working backwards through lib/coverage.js to find a better solution
This got me looking at internals.file = async function (filename, data, options) {
In this method we do one pass over the transformed file to figure out how many lines contained a statement that was not covered by the test (good so far), but then we immediately increment the hits or misses count. Then after generating the hits and misses counts, we call addSourceMapsInformation
, which is only utilized on display.
I have two thoughts at this point:
addSourceMapsInformation
and really make sure I understand internals.file
before I make any changes that accidentally break something, but it feels imminently possible to only include a line as a hit or a miss if it exists in the original source file. Technically doing so is ignoring lines of code that were not covered. This therefore shouldn't be the default behavior, but could potentially be enabled with a command-line option (something like -i
/ --ignore-transformed-files
). <sourcefile>-transformed
(so that it's clear this was the output after transformation)As an aside, since I figured out what was causing the issue, I was able to resolve it for my project by just changing the TypeScript target to es2017
(which is fine for me since I'm running Node 13)
Update: This only worked for my simple test project. In my real project I'm using TypeORM, which makes heavy use of decorators. Decorators aren't supported by any version of JavaScript, so they create the following polyfill:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
This polyfill isn't completely covered by my tests (and likely never will be) so I'm stuck with "missing coverage from file(s): null on line(s): , , ," until a PR is submitted to fix this (possibly using one of the two methods I described in my prior post)
For the record, I forked @hapi/lab
and added the following near the bottom of the internals.file
method. It's not perfect (per the comment), but it works for TypeScript sources:
// Translate source maps
if (ret.sourcemaps)
{
const mappedSource = {};
const loadedOriginalSource = {};
Object.keys(ret.source).forEach(id =>
{
const line = ret.source[id];
if (line.originalFilename)
{
// ERROR: If a file came from two original source files (e.g. concatenation)
// then we'll only include the first file in line.source and we'll
// combine the number of hits
//
// Ideally this method needs a way to return more than one file and the files
// need to be merged by whoever reads the result
//
// Although for just TypeScript-to-JavaScript, this is fine
const originalSource = loadedOriginalSource[line.originalFilename] || Fs.readFileSync(line.originalFilename, 'utf8').split("\n");
loadedOriginalSource[line.originalFilename] = originalSource;
let originalLine = mappedSource[line.originalLine] || {
source: originalSource[line.originalLine - 1],
hits: 0,
miss: false
};
originalLine.hits += (line.hits || 0);
originalLine.miss = originalLine.miss || line.miss;
mappedSource[line.originalLine] = originalLine;
}
});
ret.source = mappedSource;
ret.sloc = Object.keys(ret.source).length;
ret.hits = 0;
ret.misses = 0;
ret.sourcemaps = false;
Object.keys(ret.source).forEach(id =>
{
if (ret.source[id].miss)
{
ret.misses++;
}
else
{
ret.hits++;
}
});
}
For those looking for a workaround, we're using lab with nyc for coverage (with source-map-support
and ts-node/register
), it works pretty well.
For decorators, it seems to work reasonably (and surprisingly, it works better when using ts-node/register
instead of ts-node/register/transpile-only
when running TypeScript files directly).
Please try again with the new --typescript flag.
Support plan
Context
What are you trying to achieve or the steps to reproduce?
src/
src/
TypeScript files using--transform node_modules/lab-transform-typescript --sourcemaps
NODE_ENV=test npx lab -l --transform node_modules/lab-transform-typescript --sourcemaps -c --coverage-all -r lcov -o lcov.info
What was the result you got?
The
lcov.info
file contains lines corresponding to the internally transpiled files, not to the original TypeScript files.What result did you expect?
The
lcov.info
file should contain the lines from the original TypeScript files.Additional information
Some related issues were filed (and fixed) for the html output: