From c67ea690e3d503938c04213400dcd596800ab07f Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Wed, 1 May 2019 19:17:09 -0700 Subject: [PATCH] feat: add support for 1:1 source-maps (#85) BREAKING CHANGE: c8 will now load source-maps if possible and remap coverage accordingly --- bin/c8.js | 4 +- lib/commands/check-coverage.js | 4 +- lib/commands/report.js | 8 +- lib/report.js | 13 +-- package-lock.json | 25 +++-- package.json | 3 +- .../fixtures/source-maps/branches/branches.js | 20 ++++ .../source-maps/branches/branches.nyc.js | 2 + .../branches/branches.typescript.js | 28 ++++++ .../branches/branches.typescript.js.map | 1 + .../branches/branches.typescript.ts | 25 +++++ .../source-maps/branches/branches.uglify.js | 2 + .../branches/branches.uglify.js.map | 1 + test/fixtures/source-maps/classes/classes.js | 27 ++++++ .../source-maps/classes/classes.nyc.js | 2 + .../source-maps/classes/classes.typescript.js | 32 +++++++ .../classes/classes.typescript.js.map | 1 + .../source-maps/classes/classes.typescript.ts | 33 +++++++ .../source-maps/classes/classes.uglify.js | 2 + .../source-maps/classes/classes.uglify.js.map | 1 + test/integration.js | 96 +++++++++++++++++++ test/integration.js.snap | 84 ++++++++++++++++ 22 files changed, 388 insertions(+), 26 deletions(-) create mode 100644 test/fixtures/source-maps/branches/branches.js create mode 100644 test/fixtures/source-maps/branches/branches.nyc.js create mode 100644 test/fixtures/source-maps/branches/branches.typescript.js create mode 100644 test/fixtures/source-maps/branches/branches.typescript.js.map create mode 100644 test/fixtures/source-maps/branches/branches.typescript.ts create mode 100644 test/fixtures/source-maps/branches/branches.uglify.js create mode 100644 test/fixtures/source-maps/branches/branches.uglify.js.map create mode 100644 test/fixtures/source-maps/classes/classes.js create mode 100644 test/fixtures/source-maps/classes/classes.nyc.js create mode 100644 test/fixtures/source-maps/classes/classes.typescript.js create mode 100644 test/fixtures/source-maps/classes/classes.typescript.js.map create mode 100644 test/fixtures/source-maps/classes/classes.typescript.ts create mode 100644 test/fixtures/source-maps/classes/classes.uglify.js create mode 100644 test/fixtures/source-maps/classes/classes.uglify.js.map diff --git a/bin/c8.js b/bin/c8.js index 947f1cff..0ef88ced 100755 --- a/bin/c8.js +++ b/bin/c8.js @@ -27,8 +27,8 @@ let argv = buildYargs().parse(instrumenterArgs) } process.env.NODE_V8_COVERAGE = argv.tempDirectory - foreground(hideInstrumenterArgs(argv), done => { - outputReport(argv) + foreground(hideInstrumenterArgs(argv), async (done) => { + await outputReport(argv) done() }) } diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js index 2eb77ebd..7efe2e5f 100644 --- a/lib/commands/check-coverage.js +++ b/lib/commands/check-coverage.js @@ -25,14 +25,14 @@ exports.handler = function (argv) { exports.checkCoverages(argv, report) } -exports.checkCoverages = function (argv, report) { +exports.checkCoverages = async function (argv, report) { const thresholds = { lines: argv.lines, functions: argv.functions, branches: argv.branches, statements: argv.statements } - const map = report.getCoverageMapFromAllCoverageFiles() + const map = await report.getCoverageMapFromAllCoverageFiles() if (argv.perFile) { map.files().forEach(file => { checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file) diff --git a/lib/commands/report.js b/lib/commands/report.js index 96b1147f..cdc363b6 100644 --- a/lib/commands/report.js +++ b/lib/commands/report.js @@ -5,11 +5,11 @@ exports.command = 'report' exports.describe = 'read V8 coverage data from temp and output report' -exports.handler = function (argv) { - exports.outputReport(argv) +exports.handler = async function (argv) { + await exports.outputReport(argv) } -exports.outputReport = function (argv) { +exports.outputReport = async function (argv) { const report = Report({ include: argv.include, exclude: argv.exclude, @@ -21,6 +21,6 @@ exports.outputReport = function (argv) { omitRelative: argv.omitRelative, wrapperLength: argv.wrapperLength }) - report.run() + await report.run() if (argv.checkCoverage) checkCoverages(argv, report) } diff --git a/lib/report.js b/lib/report.js index c8914cb3..b6992c87 100644 --- a/lib/report.js +++ b/lib/report.js @@ -33,8 +33,8 @@ class Report { this.omitRelative = omitRelative this.wrapperLength = wrapperLength } - run () { - const map = this.getCoverageMapFromAllCoverageFiles() + async run () { + const map = await this.getCoverageMapFromAllCoverageFiles() var context = libReport.createContext({ dir: this.reportsDirectory, watermarks: this.watermarks @@ -47,7 +47,7 @@ class Report { }) } - getCoverageMapFromAllCoverageFiles () { + async getCoverageMapFromAllCoverageFiles () { // the merge process can be very expensive, and it's often the case that // check-coverage is called immediately after a report. We memoize the // result from getCoverageMapFromAllCoverageFiles() to address this @@ -61,9 +61,10 @@ class Report { for (const v8ScriptCov of v8ProcessCov.result) { try { const path = resolve(this.resolve, v8ScriptCov.url) - const script = v8toIstanbul(path, this.wrapperLength) - script.applyCoverage(v8ScriptCov.functions) - map.merge(script.toIstanbul()) + const converter = v8toIstanbul(path, this.wrapperLength) + await converter.load() + converter.applyCoverage(v8ScriptCov.functions) + map.merge(converter.toIstanbul()) } catch (err) { console.warn(`file: ${v8ScriptCov.url} error: ${err.stack}`) } diff --git a/package-lock.json b/package-lock.json index c9a6cc63..2bfe9bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -667,6 +667,14 @@ "q": "^1.5.1" } }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3196,8 +3204,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -3736,15 +3743,13 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, "v8-to-istanbul": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-2.0.4.tgz", - "integrity": "sha512-eAHPnQYNHlZWxfa76dUrgdBK7cTmSSoX1aPq1yH/0tImW2gAGLw2GF+hhMaKAD9RvyJUP6pfSLVmV3HkoRv5oA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-3.0.1.tgz", + "integrity": "sha512-pJkVsOvClKyXS23MIysPo1IZI99jrx+0De6LpYw31o+eR/dIGffce7RpwT+PQKbF3tsVrQHvjbxjbVrhaXz5DA==", + "requires": { + "convert-source-map": "^1.6.0" + } }, "validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index 1f1bc4d1..f19b1e78 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,7 @@ "istanbul-reports": "^2.0.0", "rimraf": "^2.6.2", "test-exclude": "^5.0.0", - "uuid": "^3.3.2", - "v8-to-istanbul": "^2.0.4", + "v8-to-istanbul": "^3.0.1", "yargs": "^13.1.0", "yargs-parser": "^10.1.0" }, diff --git a/test/fixtures/source-maps/branches/branches.js b/test/fixtures/source-maps/branches/branches.js new file mode 100644 index 00000000..73ec09c0 --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.js @@ -0,0 +1,20 @@ +if (false) { + console.info('unreachable') +} else if (true) { + console.info('reachable') +} else { + console.info('unreachable') +} + +function branch (a) { + if (a) { + console.info('a = true') + } else if (undefined) { + console.info('unreachable') + } else { + console.info('a = false') + } +} + +branch(true) +branch(false) diff --git a/test/fixtures/source-maps/branches/branches.nyc.js b/test/fixtures/source-maps/branches/branches.nyc.js new file mode 100644 index 00000000..5d71df5b --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.nyc.js @@ -0,0 +1,2 @@ +var cov_263bu3eqm8=function(){var path= "./branches.js";var hash="424788076537d051b5bf0e2564aef393124eabc7";var global=new Function("return this")();var gcv="__coverage__";var coverageData={path: "./branches.js",statementMap:{"0":{start:{line:1,column:0},end:{line:7,column:1}},"1":{start:{line:2,column:2},end:{line:2,column:29}},"2":{start:{line:3,column:7},end:{line:7,column:1}},"3":{start:{line:4,column:2},end:{line:4,column:27}},"4":{start:{line:6,column:2},end:{line:6,column:29}},"5":{start:{line:10,column:2},end:{line:16,column:3}},"6":{start:{line:11,column:4},end:{line:11,column:28}},"7":{start:{line:12,column:9},end:{line:16,column:3}},"8":{start:{line:13,column:4},end:{line:13,column:31}},"9":{start:{line:15,column:4},end:{line:15,column:29}},"10":{start:{line:19,column:0},end:{line:19,column:12}},"11":{start:{line:20,column:0},end:{line:20,column:13}}},fnMap:{"0":{name:"branch",decl:{start:{line:9,column:9},end:{line:9,column:15}},loc:{start:{line:9,column:20},end:{line:17,column:1}},line:9}},branchMap:{"0":{loc:{start:{line:1,column:0},end:{line:7,column:1}},type:"if",locations:[{start:{line:1,column:0},end:{line:7,column:1}},{start:{line:1,column:0},end:{line:7,column:1}}],line:1},"1":{loc:{start:{line:3,column:7},end:{line:7,column:1}},type:"if",locations:[{start:{line:3,column:7},end:{line:7,column:1}},{start:{line:3,column:7},end:{line:7,column:1}}],line:3},"2":{loc:{start:{line:10,column:2},end:{line:16,column:3}},type:"if",locations:[{start:{line:10,column:2},end:{line:16,column:3}},{start:{line:10,column:2},end:{line:16,column:3}}],line:10},"3":{loc:{start:{line:12,column:9},end:{line:16,column:3}},type:"if",locations:[{start:{line:12,column:9},end:{line:16,column:3}},{start:{line:12,column:9},end:{line:16,column:3}}],line:12}},s:{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},f:{"0":0},b:{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0]},_coverageSchema:"43e27e138ebf9cfc5966b082cf9a028302ed4184",hash:"424788076537d051b5bf0e2564aef393124eabc7"};var coverage=global[gcv]||(global[gcv]={});if(coverage[path]&&coverage[path].hash===hash){return coverage[path];}return coverage[path]=coverageData;}();cov_263bu3eqm8.s[0]++;if(false){cov_263bu3eqm8.b[0][0]++;cov_263bu3eqm8.s[1]++;console.info('unreachable');}else{cov_263bu3eqm8.b[0][1]++;cov_263bu3eqm8.s[2]++;if(true){cov_263bu3eqm8.b[1][0]++;cov_263bu3eqm8.s[3]++;console.info('reachable');}else{cov_263bu3eqm8.b[1][1]++;cov_263bu3eqm8.s[4]++;console.info('unreachable');}}function branch(a){cov_263bu3eqm8.f[0]++;cov_263bu3eqm8.s[5]++;if(a){cov_263bu3eqm8.b[2][0]++;cov_263bu3eqm8.s[6]++;console.info('a = true');}else{cov_263bu3eqm8.b[2][1]++;cov_263bu3eqm8.s[7]++;if(undefined){cov_263bu3eqm8.b[3][0]++;cov_263bu3eqm8.s[8]++;console.info('unreachable');}else{cov_263bu3eqm8.b[3][1]++;cov_263bu3eqm8.s[9]++;console.info('a = false');}}}cov_263bu3eqm8.s[10]++;branch(true);cov_263bu3eqm8.s[11]++;branch(false); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4vYnJhbmNoZXMuanMiXSwibmFtZXMiOlsiY29uc29sZSIsImluZm8iLCJicmFuY2giLCJhIiwidW5kZWZpbmVkIl0sIm1hcHBpbmdzIjoic3VFQUFBLEdBQUksS0FBSixDQUFXLGdEQUNUQSxPQUFPLENBQUNDLElBQVIsQ0FBYSxhQUFiLEVBQ0QsQ0FGRCxJQUVPLG1EQUFJLElBQUosQ0FBVSxnREFDZkQsT0FBTyxDQUFDQyxJQUFSLENBQWEsV0FBYixFQUNELENBRk0sSUFFQSxnREFDTEQsT0FBTyxDQUFDQyxJQUFSLENBQWEsYUFBYixFQUNELEVBRUQsUUFBU0MsQ0FBQUEsTUFBVCxDQUFpQkMsQ0FBakIsQ0FBb0IsNkNBQ2xCLEdBQUlBLENBQUosQ0FBTyxnREFDTEgsT0FBTyxDQUFDQyxJQUFSLENBQWEsVUFBYixFQUNELENBRkQsSUFFTyxtREFBSUcsU0FBSixDQUFlLGdEQUNwQkosT0FBTyxDQUFDQyxJQUFSLENBQWEsYUFBYixFQUNELENBRk0sSUFFQSxnREFDTEQsT0FBTyxDQUFDQyxJQUFSLENBQWEsV0FBYixFQUNELEVBQ0YsQyx1QkFFREMsTUFBTSxDQUFDLElBQUQsQ0FBTixDLHVCQUNBQSxNQUFNLENBQUMsS0FBRCxDQUFOIiwic291cmNlc0NvbnRlbnQiOlsiaWYgKGZhbHNlKSB7XG4gIGNvbnNvbGUuaW5mbygndW5yZWFjaGFibGUnKVxufSBlbHNlIGlmICh0cnVlKSB7XG4gIGNvbnNvbGUuaW5mbygncmVhY2hhYmxlJylcbn0gZWxzZSB7XG4gIGNvbnNvbGUuaW5mbygndW5yZWFjaGFibGUnKVxufVxuXG5mdW5jdGlvbiBicmFuY2ggKGEpIHtcbiAgaWYgKGEpIHtcbiAgICBjb25zb2xlLmluZm8oJ2EgPSB0cnVlJylcbiAgfSBlbHNlIGlmICh1bmRlZmluZWQpIHtcbiAgICBjb25zb2xlLmluZm8oJ3VucmVhY2hhYmxlJylcbiAgfSBlbHNlIHtcbiAgICBjb25zb2xlLmluZm8oJ2EgPSBmYWxzZScpXG4gIH1cbn1cblxuYnJhbmNoKHRydWUpXG5icmFuY2goZmFsc2UpXG4iXX0= diff --git a/test/fixtures/source-maps/branches/branches.typescript.js b/test/fixtures/source-maps/branches/branches.typescript.js new file mode 100644 index 00000000..4cb23ee0 --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.typescript.js @@ -0,0 +1,28 @@ +var ATrue; +(function (ATrue) { + ATrue[ATrue["IsTrue"] = 1] = "IsTrue"; + ATrue[ATrue["IsFalse"] = 0] = "IsFalse"; +})(ATrue || (ATrue = {})); +if (false) { + console.info('unreachable'); +} +else if (true) { + console.info('reachable'); +} +else { + console.info('unreachable'); +} +function branch(a) { + if (a) { + console.info('a = true'); + } + else if (undefined) { + console.info('unreachable'); + } + else { + console.info('a = false'); + } +} +branch(!!ATrue.IsTrue); +branch(!!ATrue.IsFalse); +//# sourceMappingURL=branches.typescript.js.map \ No newline at end of file diff --git a/test/fixtures/source-maps/branches/branches.typescript.js.map b/test/fixtures/source-maps/branches/branches.typescript.js.map new file mode 100644 index 00000000..312bc198 --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.typescript.js.map @@ -0,0 +1 @@ +{"version":3,"file":"branches.typescript.js","sourceRoot":"","sources":["branches.typescript.ts"],"names":[],"mappings":"AAAA,IAAK,KAGJ;AAHD,WAAK,KAAK;IACR,qCAAU,CAAA;IACV,uCAAW,CAAA;AACb,CAAC,EAHI,KAAK,KAAL,KAAK,QAGT;AAED,IAAI,KAAK,EAAE;IACT,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;CAC5B;KAAM,IAAI,IAAI,EAAE;IACf,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;CAC1B;KAAM;IACL,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;CAC5B;AAED,SAAS,MAAM,CAAE,CAAU;IACzB,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;KACzB;SAAM,IAAI,SAAS,EAAE;QACpB,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;KAC5B;SAAM;QACL,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;KAC1B;AACH,CAAC;AAED,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;AACtB,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA"} \ No newline at end of file diff --git a/test/fixtures/source-maps/branches/branches.typescript.ts b/test/fixtures/source-maps/branches/branches.typescript.ts new file mode 100644 index 00000000..18a420ab --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.typescript.ts @@ -0,0 +1,25 @@ +enum ATrue { + IsTrue = 1, + IsFalse = 0 +} + +if (false) { + console.info('unreachable') +} else if (true) { + console.info('reachable') +} else { + console.info('unreachable') +} + +function branch (a: boolean) { + if (a) { + console.info('a = true') + } else if (undefined) { + console.info('unreachable') + } else { + console.info('a = false') + } +} + +branch(!!ATrue.IsTrue) +branch(!!ATrue.IsFalse) diff --git a/test/fixtures/source-maps/branches/branches.uglify.js b/test/fixtures/source-maps/branches/branches.uglify.js new file mode 100644 index 00000000..0a4cf71f --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.uglify.js @@ -0,0 +1,2 @@ +if(false){console.info("unreachable")}else if(true){console.info("reachable")}else{console.info("unreachable")}function branch(a){if(a){console.info("a = true")}else if(undefined){console.info("unreachable")}else{console.info("a = false")}}branch(true);branch(false); +//# sourceMappingURL=./branches.uglify.js.map \ No newline at end of file diff --git a/test/fixtures/source-maps/branches/branches.uglify.js.map b/test/fixtures/source-maps/branches/branches.uglify.js.map new file mode 100644 index 00000000..d90c5817 --- /dev/null +++ b/test/fixtures/source-maps/branches/branches.uglify.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["branches.js"],"names":["console","info","branch","a","undefined"],"mappings":"AAAA,GAAI,MAAO,CACTA,QAAQC,KAAK,oBACR,GAAI,KAAM,CACfD,QAAQC,KAAK,iBACR,CACLD,QAAQC,KAAK,eAGf,SAASC,OAAQC,GACf,GAAIA,EAAG,CACLH,QAAQC,KAAK,iBACR,GAAIG,UAAW,CACpBJ,QAAQC,KAAK,mBACR,CACLD,QAAQC,KAAK,cAIjBC,OAAO,MACPA,OAAO","sourceRoot":"./"} \ No newline at end of file diff --git a/test/fixtures/source-maps/classes/classes.js b/test/fixtures/source-maps/classes/classes.js new file mode 100644 index 00000000..e1637d52 --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.js @@ -0,0 +1,27 @@ +class Foo { + constructor (x=33) { + this.x = x ? x : 99 + if (this.x) { + console.info('covered') + } else { + console.info('uncovered') + } + this.methodC() + } + methodA () { + console.info('covered') + } + methodB () { + console.info('uncovered') + } + methodC () { + console.info('covered') + } + methodD () { + console.info('uncovered') + } +} + +const a = new Foo(0) +const b = new Foo(33) +a.methodA() diff --git a/test/fixtures/source-maps/classes/classes.nyc.js b/test/fixtures/source-maps/classes/classes.nyc.js new file mode 100644 index 00000000..ac5b2c9e --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.nyc.js @@ -0,0 +1,2 @@ +var cov_2p08rjaqat=function(){var path= "./classes.js";var hash="d4ec261908798ce4a62b28eecbaa34c594451ddc";var global=new Function("return this")();var gcv="__coverage__";var coverageData={path: "./classes.js",statementMap:{"0":{start:{line:3,column:4},end:{line:3,column:23}},"1":{start:{line:4,column:4},end:{line:8,column:5}},"2":{start:{line:5,column:6},end:{line:5,column:29}},"3":{start:{line:7,column:6},end:{line:7,column:31}},"4":{start:{line:9,column:4},end:{line:9,column:18}},"5":{start:{line:12,column:4},end:{line:12,column:27}},"6":{start:{line:15,column:4},end:{line:15,column:29}},"7":{start:{line:18,column:4},end:{line:18,column:27}},"8":{start:{line:21,column:4},end:{line:21,column:29}},"9":{start:{line:25,column:10},end:{line:25,column:20}},"10":{start:{line:26,column:10},end:{line:26,column:21}},"11":{start:{line:27,column:0},end:{line:27,column:11}}},fnMap:{"0":{name:"(anonymous_0)",decl:{start:{line:2,column:2},end:{line:2,column:3}},loc:{start:{line:2,column:21},end:{line:10,column:3}},line:2},"1":{name:"(anonymous_1)",decl:{start:{line:11,column:2},end:{line:11,column:3}},loc:{start:{line:11,column:13},end:{line:13,column:3}},line:11},"2":{name:"(anonymous_2)",decl:{start:{line:14,column:2},end:{line:14,column:3}},loc:{start:{line:14,column:13},end:{line:16,column:3}},line:14},"3":{name:"(anonymous_3)",decl:{start:{line:17,column:2},end:{line:17,column:3}},loc:{start:{line:17,column:13},end:{line:19,column:3}},line:17},"4":{name:"(anonymous_4)",decl:{start:{line:20,column:2},end:{line:20,column:3}},loc:{start:{line:20,column:13},end:{line:22,column:3}},line:20}},branchMap:{"0":{loc:{start:{line:2,column:15},end:{line:2,column:19}},type:"default-arg",locations:[{start:{line:2,column:17},end:{line:2,column:19}}],line:2},"1":{loc:{start:{line:3,column:13},end:{line:3,column:23}},type:"cond-expr",locations:[{start:{line:3,column:17},end:{line:3,column:18}},{start:{line:3,column:21},end:{line:3,column:23}}],line:3},"2":{loc:{start:{line:4,column:4},end:{line:8,column:5}},type:"if",locations:[{start:{line:4,column:4},end:{line:8,column:5}},{start:{line:4,column:4},end:{line:8,column:5}}],line:4}},s:{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},f:{"0":0,"1":0,"2":0,"3":0,"4":0},b:{"0":[0],"1":[0,0],"2":[0,0]},_coverageSchema:"43e27e138ebf9cfc5966b082cf9a028302ed4184",hash:"d4ec261908798ce4a62b28eecbaa34c594451ddc"};var coverage=global[gcv]||(global[gcv]={});if(coverage[path]&&coverage[path].hash===hash){return coverage[path];}return coverage[path]=coverageData;}();class Foo{constructor(x=(cov_2p08rjaqat.b[0][0]++,33)){cov_2p08rjaqat.f[0]++;cov_2p08rjaqat.s[0]++;this.x=x?(cov_2p08rjaqat.b[1][0]++,x):(cov_2p08rjaqat.b[1][1]++,99);cov_2p08rjaqat.s[1]++;if(this.x){cov_2p08rjaqat.b[2][0]++;cov_2p08rjaqat.s[2]++;console.info('covered');}else{cov_2p08rjaqat.b[2][1]++;cov_2p08rjaqat.s[3]++;console.info('uncovered');}cov_2p08rjaqat.s[4]++;this.methodC();}methodA(){cov_2p08rjaqat.f[1]++;cov_2p08rjaqat.s[5]++;console.info('covered');}methodB(){cov_2p08rjaqat.f[2]++;cov_2p08rjaqat.s[6]++;console.info('uncovered');}methodC(){cov_2p08rjaqat.f[3]++;cov_2p08rjaqat.s[7]++;console.info('covered');}methodD(){cov_2p08rjaqat.f[4]++;cov_2p08rjaqat.s[8]++;console.info('uncovered');}}const a=(cov_2p08rjaqat.s[9]++,new Foo(0));const b=(cov_2p08rjaqat.s[10]++,new Foo(33));cov_2p08rjaqat.s[11]++;a.methodA(); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4vY2xhc3Nlcy5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zdHJ1Y3RvciIsIngiLCJjb25zb2xlIiwiaW5mbyIsIm1ldGhvZEMiLCJtZXRob2RBIiwibWV0aG9kQiIsIm1ldGhvZEQiLCJhIiwiYiJdLCJtYXBwaW5ncyI6IjhsRkFBQSxLQUFNQSxDQUFBQSxHQUFJLENBQ1JDLFdBQVcsQ0FBRUMsQ0FBQywyQkFBQyxFQUFELENBQUgsQ0FBUSw2Q0FDakIsS0FBS0EsQ0FBTCxDQUFTQSxDQUFDLDJCQUFHQSxDQUFILDRCQUFPLEVBQVAsQ0FBVixDQURpQixzQkFFakIsR0FBSSxLQUFLQSxDQUFULENBQVksZ0RBQ1ZDLE9BQU8sQ0FBQ0MsSUFBUixDQUFhLFNBQWIsRUFDRCxDQUZELElBRU8sZ0RBQ0xELE9BQU8sQ0FBQ0MsSUFBUixDQUFhLFdBQWIsRUFDRCxDQU5nQixzQkFPakIsS0FBS0MsT0FBTCxHQUNELENBQ0RDLE9BQU8sRUFBSSw2Q0FDVEgsT0FBTyxDQUFDQyxJQUFSLENBQWEsU0FBYixFQUNELENBQ0RHLE9BQU8sRUFBSSw2Q0FDVEosT0FBTyxDQUFDQyxJQUFSLENBQWEsV0FBYixFQUNELENBQ0RDLE9BQU8sRUFBSSw2Q0FDVEYsT0FBTyxDQUFDQyxJQUFSLENBQWEsU0FBYixFQUNELENBQ0RJLE9BQU8sRUFBSSw2Q0FDVEwsT0FBTyxDQUFDQyxJQUFSLENBQWEsV0FBYixFQUNELENBckJPLENBd0JWLEtBQU1LLENBQUFBLENBQUMsd0JBQUcsR0FBSVQsQ0FBQUEsR0FBSixDQUFRLENBQVIsQ0FBSCxDQUFQLENBQ0EsS0FBTVUsQ0FBQUEsQ0FBQyx5QkFBRyxHQUFJVixDQUFBQSxHQUFKLENBQVEsRUFBUixDQUFILENBQVAsQyx1QkFDQVMsQ0FBQyxDQUFDSCxPQUFGIiwic291cmNlc0NvbnRlbnQiOlsiY2xhc3MgRm9vIHtcbiAgY29uc3RydWN0b3IgKHg9MzMpIHtcbiAgICB0aGlzLnggPSB4ID8geCA6IDk5XG4gICAgaWYgKHRoaXMueCkge1xuICAgICAgY29uc29sZS5pbmZvKCdjb3ZlcmVkJylcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc29sZS5pbmZvKCd1bmNvdmVyZWQnKVxuICAgIH1cbiAgICB0aGlzLm1ldGhvZEMoKVxuICB9XG4gIG1ldGhvZEEgKCkge1xuICAgIGNvbnNvbGUuaW5mbygnY292ZXJlZCcpXG4gIH1cbiAgbWV0aG9kQiAoKSB7XG4gICAgY29uc29sZS5pbmZvKCd1bmNvdmVyZWQnKVxuICB9XG4gIG1ldGhvZEMgKCkge1xuICAgIGNvbnNvbGUuaW5mbygnY292ZXJlZCcpXG4gIH1cbiAgbWV0aG9kRCAoKSB7XG4gICAgY29uc29sZS5pbmZvKCd1bmNvdmVyZWQnKVxuICB9XG59XG5cbmNvbnN0IGEgPSBuZXcgRm9vKDApXG5jb25zdCBiID0gbmV3IEZvbygzMylcbmEubWV0aG9kQSgpXG4iXX0= diff --git a/test/fixtures/source-maps/classes/classes.typescript.js b/test/fixtures/source-maps/classes/classes.typescript.js new file mode 100644 index 00000000..a8fe2674 --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.typescript.js @@ -0,0 +1,32 @@ +"use strict"; +exports.__esModule = true; +var Foo = /** @class */ (function () { + function Foo(options) { + this.x = options.x ? options.x : 99; + if (this.x) { + console.info('covered'); + } + else { + console.info('uncovered'); + } + this.methodC(); + } + Foo.prototype.methodA = function () { + console.info('covered'); + return 33; + }; + Foo.prototype.methodB = function () { + console.info('uncovered'); + }; + Foo.prototype.methodC = function () { + console.info('covered'); + }; + Foo.prototype.methodD = function () { + console.info('uncovered'); + }; + return Foo; +}()); +var a = new Foo({ x: 0 }); +var b = new Foo({ x: 33 }); +a.methodA(); +//# sourceMappingURL=classes.typescript.js.map \ No newline at end of file diff --git a/test/fixtures/source-maps/classes/classes.typescript.js.map b/test/fixtures/source-maps/classes/classes.typescript.js.map new file mode 100644 index 00000000..885e7b0d --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.typescript.js.map @@ -0,0 +1 @@ +{"version":3,"file":"classes.typescript.js","sourceRoot":"","sources":["classes.typescript.ts"],"names":[],"mappings":";;AAIA;IAEE,aAAa,OAAmB;QAC9B,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QACnC,IAAI,IAAI,CAAC,CAAC,EAAE;YACV,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;SACxB;aAAM;YACL,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;SAC1B;QACD,IAAI,CAAC,OAAO,EAAE,CAAA;IAChB,CAAC;IACD,qBAAO,GAAP;QACE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,qBAAO,GAAP;QACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3B,CAAC;IACO,qBAAO,GAAf;QACE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACzB,CAAC;IACD,qBAAO,GAAP;QACE,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IAC3B,CAAC;IACH,UAAC;AAAD,CAAC,AAxBD,IAwBC;AAED,IAAM,CAAC,GAAG,IAAI,GAAG,CAAC,EAAC,CAAC,EAAE,CAAC,EAAC,CAAC,CAAA;AACzB,IAAM,CAAC,GAAG,IAAI,GAAG,CAAC,EAAC,CAAC,EAAE,EAAE,EAAC,CAAC,CAAA;AAC1B,CAAC,CAAC,OAAO,EAAE,CAAA"} \ No newline at end of file diff --git a/test/fixtures/source-maps/classes/classes.typescript.ts b/test/fixtures/source-maps/classes/classes.typescript.ts new file mode 100644 index 00000000..585cb501 --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.typescript.ts @@ -0,0 +1,33 @@ +export interface FooOptions { + x: number; +} + +class Foo { + x: number; + constructor (options: FooOptions) { + this.x = options.x ? options.x : 99 + if (this.x) { + console.info('covered') + } else { + console.info('uncovered') + } + this.methodC() + } + methodA (): number { + console.info('covered') + return 33 + } + methodB () { + console.info('uncovered') + } + private methodC () { + console.info('covered') + } + methodD () { + console.info('uncovered') + } +} + +const a = new Foo({x: 0}) +const b = new Foo({x: 33}) +a.methodA() diff --git a/test/fixtures/source-maps/classes/classes.uglify.js b/test/fixtures/source-maps/classes/classes.uglify.js new file mode 100644 index 00000000..ca2786e7 --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.uglify.js @@ -0,0 +1,2 @@ +class Foo{constructor(x=33){this.x=x?x:99;if(this.x){console.info("covered")}else{console.info("uncovered")}this.methodC()}methodA(){console.info("covered")}methodB(){console.info("uncovered")}methodC(){console.info("covered")}methodD(){console.info("uncovered")}}const a=new Foo(0);const b=new Foo(33);a.methodA(); +//# sourceMappingURL=./classes.uglify.js.map \ No newline at end of file diff --git a/test/fixtures/source-maps/classes/classes.uglify.js.map b/test/fixtures/source-maps/classes/classes.uglify.js.map new file mode 100644 index 00000000..6d043748 --- /dev/null +++ b/test/fixtures/source-maps/classes/classes.uglify.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["classes.js"],"names":["Foo","[object Object]","x","this","console","info","methodC","a","b","methodA"],"mappings":"MAAMA,IACJC,YAAaC,EAAE,IACbC,KAAKD,EAAIA,EAAIA,EAAI,GACjB,GAAIC,KAAKD,EAAG,CACVE,QAAQC,KAAK,eACR,CACLD,QAAQC,KAAK,aAEfF,KAAKG,UAEPL,UACEG,QAAQC,KAAK,WAEfJ,UACEG,QAAQC,KAAK,aAEfJ,UACEG,QAAQC,KAAK,WAEfJ,UACEG,QAAQC,KAAK,cAIjB,MAAME,EAAI,IAAIP,IAAI,GAClB,MAAMQ,EAAI,IAAIR,IAAI,IAClBO,EAAEE","sourceRoot":"./classes.uglify.js"} \ No newline at end of file diff --git a/test/integration.js b/test/integration.js index 90a8b066..7e8feb4d 100644 --- a/test/integration.js +++ b/test/integration.js @@ -171,4 +171,100 @@ describe('c8', () => { output.toString('utf8').should.matchSnapshot() }) }) + + describe('source-maps', () => { + beforeEach(cb => rimraf('tmp/source-map', cb)) + + describe('TypeScript', () => { + // Bugs: + // closing '}' on `if` is not covered. + it('remaps branches', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/branches/branches.typescript.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + + // Bugs: + // closing '}' on `if` is not covered. + it('remaps classes', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/classes/classes.typescript.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + }) + + describe('UglifyJS', () => { + // Bugs: + // string in `console.info` shown as uncovered branch. + it('remaps branches', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/branches/branches.uglify.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + + // Bugs: + // string in `console.info` shown as uncovered branch. + it('remaps classes', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/classes/classes.uglify.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + }) + + describe('nyc', () => { + // Bugs: + // first 'if' statement indicates two odd missing branches. + // line 4 should be covered. + it('remaps branches', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/branches/branches.nyc.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + + // Bugs: + // the portion `class F` of `class Foo` indicates missing branch. + // line 6 should be covered. + it('remaps classes', () => { + const { output } = spawnSync(nodePath, [ + c8Path, + '--exclude="test/*.js"', + '--temp-directory=tmp/source-map', + '--clean=true', + nodePath, + require.resolve('./fixtures/source-maps/classes/classes.nyc.js') + ]) + output.toString('utf8').should.matchSnapshot() + }) + }) + }) }) diff --git a/test/integration.js.snap b/test/integration.js.snap index 187d0279..fecf3eb1 100644 --- a/test/integration.js.snap +++ b/test/integration.js.snap @@ -101,3 +101,87 @@ All files | 83.33 | 85.71 | 60 | 83.33 | | -----------|----------|----------|----------|----------|-------------------| ," `; + +exports[`c8 source-maps TypeScript remaps branches 1`] = ` +",reachable +a = true +a = false +------------------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +------------------------|----------|----------|----------|----------|-------------------| +All files | 84 | 57.14 | 100 | 84 | | + branches.typescript.ts | 84 | 57.14 | 100 | 84 | 7,11,12,18 | +------------------------|----------|----------|----------|----------|-------------------| +," +`; + +exports[`c8 source-maps TypeScript remaps classes 1`] = ` +",covered +covered +covered +covered +covered +-----------------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +-----------------------|----------|----------|----------|----------|-------------------| +All files | 81.82 | 87.5 | 60 | 81.82 | | + classes.typescript.ts | 81.82 | 87.5 | 60 | 81.82 | 12,13,21,22,27,28 | +-----------------------|----------|----------|----------|----------|-------------------| +," +`; + +exports[`c8 source-maps UglifyJS remaps branches 1`] = ` +",reachable +a = true +a = false +-------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +-------------|----------|----------|----------|----------|-------------------| +All files | 80 | 50 | 100 | 80 | | + branches.js | 80 | 50 | 100 | 80 | 2,5,6,13 | +-------------|----------|----------|----------|----------|-------------------| +," +`; + +exports[`c8 source-maps UglifyJS remaps classes 1`] = ` +",covered +covered +covered +covered +covered +------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +------------|----------|----------|----------|----------|-------------------| +All files | 85.19 | 83.33 | 60 | 85.19 | | + classes.js | 85.19 | 83.33 | 60 | 85.19 | 6,7,15,21 | +------------|----------|----------|----------|----------|-------------------| +," +`; + +exports[`c8 source-maps nyc remaps branches 1`] = ` +",reachable +a = true +a = false +-------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +-------------|----------|----------|----------|----------|-------------------| +All files | 70 | 44.44 | 100 | 70 | | + branches.js | 70 | 44.44 | 100 | 70 | 2,4,5,6,7,13 | +-------------|----------|----------|----------|----------|-------------------| +," +`; + +exports[`c8 source-maps nyc remaps classes 1`] = ` +",covered +covered +covered +covered +covered +------------|----------|----------|----------|----------|-------------------| +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | +------------|----------|----------|----------|----------|-------------------| +All files | 70.37 | 66.67 | 60 | 70.37 | | + classes.js | 70.37 | 66.67 | 60 | 70.37 |... ,8,15,16,21,22 | +------------|----------|----------|----------|----------|-------------------| +," +`;