diff --git a/packages/istanbul-lib-source-maps/lib/transformer.js b/packages/istanbul-lib-source-maps/lib/transformer.js index 3d26c515..6f635383 100644 --- a/packages/istanbul-lib-source-maps/lib/transformer.js +++ b/packages/istanbul-lib-source-maps/lib/transformer.js @@ -14,6 +14,7 @@ class SourceMapTransformer { constructor(finder, opts = {}) { this.finder = finder; this.baseDir = opts.baseDir || process.cwd(); + this.resolveMapping = opts.getMapping || getMapping; } processFile(fc, sourceMap, coverageMapper) { @@ -21,7 +22,7 @@ class SourceMapTransformer { Object.entries(fc.statementMap).forEach(([s, loc]) => { const hits = fc.s[s]; - const mapping = getMapping(sourceMap, loc, fc.path); + const mapping = this.resolveMapping(sourceMap, loc, fc.path); if (mapping) { changes += 1; @@ -32,8 +33,17 @@ class SourceMapTransformer { Object.entries(fc.fnMap).forEach(([f, fnMeta]) => { const hits = fc.f[f]; - const mapping = getMapping(sourceMap, fnMeta.decl, fc.path); - const spanMapping = getMapping(sourceMap, fnMeta.loc, fc.path); + const mapping = this.resolveMapping( + sourceMap, + fnMeta.decl, + fc.path + ); + + const spanMapping = this.resolveMapping( + sourceMap, + fnMeta.loc, + fc.path + ); if ( mapping && @@ -59,7 +69,7 @@ class SourceMapTransformer { let skip; branchMeta.locations.forEach((loc, i) => { - const mapping = getMapping(sourceMap, loc, fc.path); + const mapping = this.resolveMapping(sourceMap, loc, fc.path); if (mapping) { if (!source) { source = mapping.source; @@ -74,12 +84,16 @@ class SourceMapTransformer { } }); + const locMapping = branchMeta.loc + ? this.resolveMapping(sourceMap, branchMeta.loc, fc.path) + : null; + if (!skip && locs.length > 0) { changes += 1; const mappedCoverage = coverageMapper(source); mappedCoverage.addBranch( branchMeta.type, - locs[0] /* XXX */, + locMapping ? locMapping.loc : locs[0], locs, mappedHits ); diff --git a/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageData.json b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageData.json new file mode 100644 index 00000000..671b3f41 --- /dev/null +++ b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageData.json @@ -0,0 +1,408 @@ +{ + "path": "dummyFile.js", + "statementMap": { + "0": { + "start": { + "line": 2, + "column": 0 + }, + "end": { + "line": 2, + "column": 62 + } + }, + "1": { + "start": { + "line": 3, + "column": 29 + }, + "end": { + "line": 3, + "column": 60 + } + }, + "2": { + "start": { + "line": 4, + "column": 29 + }, + "end": { + "line": 4, + "column": 60 + } + }, + "3": { + "start": { + "line": 5, + "column": 30 + }, + "end": { + "line": 5, + "column": 62 + } + }, + "4": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 45 + } + }, + "5": { + "start": { + "line": 9, + "column": 8 + }, + "end": { + "line": 9, + "column": 29 + } + }, + "6": { + "start": { + "line": 12, + "column": 8 + }, + "end": { + "line": 12, + "column": 107 + } + }, + "7": { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + } + }, + "8": { + "start": { + "line": 17, + "column": 12 + }, + "end": { + "line": 17, + "column": 24 + } + }, + "9": { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + } + }, + "10": { + "start": { + "line": 22, + "column": 12 + }, + "end": { + "line": 22, + "column": 111 + } + }, + "11": { + "start": { + "line": 24, + "column": 8 + }, + "end": { + "line": 32, + "column": 9 + } + }, + "12": { + "start": { + "line": 27, + "column": 16 + }, + "end": { + "line": 27, + "column": 115 + } + }, + "13": { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 116 + } + }, + "14": { + "start": { + "line": 31, + "column": 16 + }, + "end": { + "line": 31, + "column": 115 + } + }, + "15": { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 35, + "column": 37 + } + } + }, + "fnMap": { + "0": { + "name": "(anonymous_0)", + "decl": { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 5 + } + }, + "loc": { + "start": { + "line": 7, + "column": 40 + }, + "end": { + "line": 10, + "column": 5 + } + }, + "line": 7 + }, + "1": { + "name": "(anonymous_1)", + "decl": { + "start": { + "line": 11, + "column": 4 + }, + "end": { + "line": 11, + "column": 5 + } + }, + "loc": { + "start": { + "line": 11, + "column": 48 + }, + "end": { + "line": 13, + "column": 5 + } + }, + "line": 11 + }, + "2": { + "name": "(anonymous_2)", + "decl": { + "start": { + "line": 14, + "column": 4 + }, + "end": { + "line": 14, + "column": 5 + } + }, + "loc": { + "start": { + "line": 14, + "column": 41 + }, + "end": { + "line": 33, + "column": 5 + } + }, + "line": 14 + } + }, + "branchMap": { + "0": { + "loc": { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + } + }, + "type": "if", + "locations": [ + { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + } + }, + { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + } + } + ], + "line": 15 + }, + "1": { + "loc": { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + } + }, + "type": "if", + "locations": [ + { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + } + }, + { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + } + } + ], + "line": 19 + }, + "2": { + "loc": { + "start": { + "line": 24, + "column": 8 + }, + "end": { + "line": 32, + "column": 9 + } + }, + "type": "switch", + "locations": [ + { + "start": { + "line": 25, + "column": 12 + }, + "end": { + "line": 25, + "column": 27 + } + }, + { + "start": { + "line": 26, + "column": 12 + }, + "end": { + "line": 27, + "column": 115 + } + }, + { + "start": { + "line": 28, + "column": 12 + }, + "end": { + "line": 29, + "column": 116 + } + }, + { + "start": { + "line": 30, + "column": 12 + }, + "end": { + "line": 31, + "column": 115 + } + } + ], + "line": 24 + } + }, + "s": { + "0": 1, + "1": 1, + "2": 1, + "3": 1, + "4": 3, + "5": 3, + "6": 0, + "7": 0, + "8": 0, + "9": 0, + "10": 0, + "11": 0, + "12": 0, + "13": 0, + "14": 0, + "15": 1 + }, + "f": { + "0": 3, + "1": 0, + "2": 0 + }, + "b": { + "0": [ + 0, + 0 + ], + "1": [ + 0, + 0 + ], + "2": [ + 0, + 0, + 0, + 0 + ] + } +} diff --git a/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataExpectedResult.json b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataExpectedResult.json new file mode 100644 index 00000000..805ddd6d --- /dev/null +++ b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataExpectedResult.json @@ -0,0 +1,391 @@ +{ + "path": "dummyFile.ts", + "statementMap": { + "0": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 54 + } + }, + "1": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 54 + } + }, + "2": { + "start": { + "line": 9, + "column": 0 + }, + "end": { + "line": 9, + "column": 56 + } + }, + "3": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 45 + } + }, + "4": { + "start": { + "line": 17, + "column": 8 + }, + "end": { + "line": 17, + "column": 29 + } + }, + "5": { + "start": { + "line": 21, + "column": 8 + }, + "end": { + "line": 21, + "column": 110 + } + }, + "6": { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + }, + "7": { + "start": { + "line": 27, + "column": 12 + }, + "end": { + "line": 27, + "column": 24 + } + }, + "8": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + }, + "9": { + "start": { + "line": 32, + "column": 12 + }, + "end": { + "line": 32, + "column": 114 + } + }, + "10": { + "start": { + "line": 35, + "column": 8 + }, + "end": { + "line": 43, + "column": null + } + }, + "11": { + "start": { + "line": 38, + "column": 16 + }, + "end": { + "line": 38, + "column": 118 + } + }, + "12": { + "start": { + "line": 40, + "column": 16 + }, + "end": { + "line": 40, + "column": 119 + } + }, + "13": { + "start": { + "line": 42, + "column": 16 + }, + "end": { + "line": 42, + "column": 118 + } + }, + "14": { + "start": { + "line": 11, + "column": 0 + }, + "end": { + "line": 11, + "column": 21 + } + } + }, + "fnMap": { + "0": { + "name": "(anonymous_0)", + "decl": { + "start": { + "line": 15, + "column": 4 + }, + "end": { + "line": 15, + "column": 16 + } + }, + "loc": { + "start": { + "line": 15, + "column": 64 + }, + "end": { + "line": 18, + "column": 5 + } + } + }, + "1": { + "name": "(anonymous_1)", + "decl": { + "start": { + "line": 20, + "column": 11 + }, + "end": { + "line": 20, + "column": 29 + } + }, + "loc": { + "start": { + "line": 20, + "column": 80 + }, + "end": { + "line": 22, + "column": 5 + } + } + }, + "2": { + "name": "(anonymous_2)", + "decl": { + "start": { + "line": 24, + "column": 11 + }, + "end": { + "line": 24, + "column": 22 + } + }, + "loc": { + "start": { + "line": 24, + "column": 73 + }, + "end": { + "line": 44, + "column": 5 + } + } + } + }, + "branchMap": { + "0": { + "loc": { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + }, + "type": "if", + "locations": [ + { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + }, + { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + } + ] + }, + "1": { + "loc": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + }, + "type": "if", + "locations": [ + { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + }, + { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + } + ] + }, + "2": { + "loc": { + "start": { + "line": 35, + "column": 8 + }, + "end": { + "line": 43, + "column": null + } + }, + "type": "switch", + "locations": [ + { + "start": { + "line": 36, + "column": 12 + }, + "end": { + "line": 36, + "column": 27 + } + }, + { + "start": { + "line": 37, + "column": 12 + }, + "end": { + "line": 38, + "column": 118 + } + }, + { + "start": { + "line": 39, + "column": 12 + }, + "end": { + "line": 40, + "column": 119 + } + }, + { + "start": { + "line": 41, + "column": 12 + }, + "end": { + "line": 42, + "column": 118 + } + } + ] + } + }, + "s": { + "0": 1, + "1": 1, + "2": 1, + "3": 3, + "4": 3, + "5": 0, + "6": 0, + "7": 0, + "8": 0, + "9": 0, + "10": 0, + "11": 0, + "12": 0, + "13": 0, + "14": 1 + }, + "f": { + "0": 3, + "1": 0, + "2": 0 + }, + "b": { + "0": [ + 0, + 0 + ], + "1": [ + 0, + 0 + ], + "2": [ + 0, + 0, + 0, + 0 + ] + } +} \ No newline at end of file diff --git a/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataMapping.json b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataMapping.json new file mode 100644 index 00000000..3258673d --- /dev/null +++ b/packages/istanbul-lib-source-maps/test/testdata/switchBranchCoverageDataMapping.json @@ -0,0 +1,562 @@ +[ + { + "start": { + "line": 3, + "column": 29 + }, + "end": { + "line": 3, + "column": 60 + }, + "mapped": { + "start": { + "line": 7, + "column": 0 + }, + "end": { + "line": 7, + "column": 54 + } + } + }, + { + "start": { + "line": 4, + "column": 29 + }, + "end": { + "line": 4, + "column": 60 + }, + "mapped": { + "start": { + "line": 8, + "column": 0 + }, + "end": { + "line": 8, + "column": 54 + } + } + }, + { + "start": { + "line": 5, + "column": 30 + }, + "end": { + "line": 5, + "column": 62 + }, + "mapped": { + "start": { + "line": 9, + "column": 0 + }, + "end": { + "line": 9, + "column": 56 + } + } + }, + { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 45 + }, + "mapped": { + "start": { + "line": 16, + "column": 8 + }, + "end": { + "line": 16, + "column": 45 + } + } + }, + { + "start": { + "line": 9, + "column": 8 + }, + "end": { + "line": 9, + "column": 29 + }, + "mapped": { + "start": { + "line": 17, + "column": 8 + }, + "end": { + "line": 17, + "column": 29 + } + } + }, + { + "start": { + "line": 12, + "column": 8 + }, + "end": { + "line": 12, + "column": 107 + }, + "mapped": { + "start": { + "line": 21, + "column": 8 + }, + "end": { + "line": 21, + "column": 110 + } + } + }, + { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + }, + "mapped": { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + } + }, + { + "start": { + "line": 17, + "column": 12 + }, + "end": { + "line": 17, + "column": 24 + }, + "mapped": { + "start": { + "line": 27, + "column": 12 + }, + "end": { + "line": 27, + "column": 24 + } + } + }, + { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + }, + "mapped": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + } + }, + { + "start": { + "line": 22, + "column": 12 + }, + "end": { + "line": 22, + "column": 111 + }, + "mapped": { + "start": { + "line": 32, + "column": 12 + }, + "end": { + "line": 32, + "column": 114 + } + } + }, + { + "start": { + "line": 24, + "column": 8 + }, + "end": { + "line": 32, + "column": 9 + }, + "mapped": { + "start": { + "line": 35, + "column": 8 + }, + "end": { + "line": 43, + "column": null + } + } + }, + { + "start": { + "line": 27, + "column": 16 + }, + "end": { + "line": 27, + "column": 115 + }, + "mapped": { + "start": { + "line": 38, + "column": 16 + }, + "end": { + "line": 38, + "column": 118 + } + } + }, + { + "start": { + "line": 29, + "column": 16 + }, + "end": { + "line": 29, + "column": 116 + }, + "mapped": { + "start": { + "line": 40, + "column": 16 + }, + "end": { + "line": 40, + "column": 119 + } + } + }, + { + "start": { + "line": 31, + "column": 16 + }, + "end": { + "line": 31, + "column": 115 + }, + "mapped": { + "start": { + "line": 42, + "column": 16 + }, + "end": { + "line": 42, + "column": 118 + } + } + }, + { + "start": { + "line": 35, + "column": 0 + }, + "end": { + "line": 35, + "column": 37 + }, + "mapped": { + "start": { + "line": 11, + "column": 0 + }, + "end": { + "line": 11, + "column": 21 + } + } + }, + { + "start": { + "line": 7, + "column": 4 + }, + "end": { + "line": 7, + "column": 5 + }, + "mapped": { + "start": { + "line": 15, + "column": 4 + }, + "end": { + "line": 15, + "column": 16 + } + } + }, + { + "start": { + "line": 7, + "column": 40 + }, + "end": { + "line": 10, + "column": 5 + }, + "mapped": { + "start": { + "line": 15, + "column": 64 + }, + "end": { + "line": 18, + "column": 5 + } + } + }, + { + "start": { + "line": 11, + "column": 4 + }, + "end": { + "line": 11, + "column": 5 + }, + "mapped": { + "start": { + "line": 20, + "column": 11 + }, + "end": { + "line": 20, + "column": 29 + } + } + }, + { + "start": { + "line": 11, + "column": 48 + }, + "end": { + "line": 13, + "column": 5 + }, + "mapped": { + "start": { + "line": 20, + "column": 80 + }, + "end": { + "line": 22, + "column": 5 + } + } + }, + { + "start": { + "line": 14, + "column": 4 + }, + "end": { + "line": 14, + "column": 5 + }, + "mapped": { + "start": { + "line": 24, + "column": 11 + }, + "end": { + "line": 24, + "column": 22 + } + } + }, + { + "start": { + "line": 14, + "column": 41 + }, + "end": { + "line": 33, + "column": 5 + }, + "mapped": { + "start": { + "line": 24, + "column": 73 + }, + "end": { + "line": 44, + "column": 5 + } + } + }, + { + "start": { + "line": 15, + "column": 8 + }, + "end": { + "line": 18, + "column": 9 + }, + "mapped": { + "start": { + "line": 25, + "column": 8 + }, + "end": { + "line": 28, + "column": null + } + } + }, + { + "start": { + "line": 19, + "column": 8 + }, + "end": { + "line": 23, + "column": 9 + }, + "mapped": { + "start": { + "line": 29, + "column": 8 + }, + "end": { + "line": 33, + "column": null + } + } + }, + { + "start": { + "line": 24, + "column": 8 + }, + "end": { + "line": 32, + "column": 9 + }, + "mapped": { + "start": { + "line": 35, + "column": 8 + }, + "end": { + "line": 43, + "column": null + } + } + }, + { + "start": { + "line": 25, + "column": 12 + }, + "end": { + "line": 25, + "column": 27 + }, + "mapped": { + "start": { + "line": 36, + "column": 12 + }, + "end": { + "line": 36, + "column": 27 + } + } + }, + { + "start": { + "line": 26, + "column": 12 + }, + "end": { + "line": 27, + "column": 115 + }, + "mapped": { + "start": { + "line": 37, + "column": 12 + }, + "end": { + "line": 38, + "column": 118 + } + } + }, + { + "start": { + "line": 28, + "column": 12 + }, + "end": { + "line": 29, + "column": 116 + }, + "mapped": { + "start": { + "line": 39, + "column": 12 + }, + "end": { + "line": 40, + "column": 119 + } + } + }, + { + "start": { + "line": 30, + "column": 12 + }, + "end": { + "line": 31, + "column": 115 + }, + "mapped": { + "start": { + "line": 41, + "column": 12 + }, + "end": { + "line": 42, + "column": 118 + } + } + } +] diff --git a/packages/istanbul-lib-source-maps/test/transformer.test.js b/packages/istanbul-lib-source-maps/test/transformer.test.js index 6de8dc1f..d419f008 100644 --- a/packages/istanbul-lib-source-maps/test/transformer.test.js +++ b/packages/istanbul-lib-source-maps/test/transformer.test.js @@ -50,6 +50,34 @@ const testDataBackslash = { } }; +class MappingResolver { + constructor(mappingFile) { + this.mapping = require(mappingFile); + } + + resolveMapping(sourceMap, location, filePath) { + const mapResult = this.mapping.find( + m => + m.start.line == location.start.line && + m.start.column == location.start.column && + m.end.line == location.end.line && + m.end.column == location.end.column + ); + + if (!mapResult) { + return null; + } + + return { + source: filePath.replace('.js', '.ts'), + loc: { + start: mapResult.mapped.start, + end: mapResult.mapped.end + } + }; + } +} + describe('transformer', () => { it('maps statement locations', async () => { const coverageMap = createMap({}); @@ -91,4 +119,25 @@ describe('transformer', () => { assert.equal(Object.keys(mapped.data).length, 1); assert.isDefined(mapped.data[testDataBackslash.coverageData.path]); }); + + it('correctly maps location for branch statements', async () => { + const switchBranchCoverageData = require('./testdata/switchBranchCoverageData.json'); + const switchBranchCoverageDataExpectedResult = require('./testdata/switchBranchCoverageDataExpectedResult.json'); + const mappingResolver = new MappingResolver( + './testdata/switchBranchCoverageDataMapping.json' + ); + + const coverageMap = createMap({}); + coverageMap.addFileCoverage(switchBranchCoverageData); + + const transformer = new SourceMapTransformer(file => ({ file }), { + getMapping: mappingResolver.resolveMapping.bind(mappingResolver) + }); + const mapped = await transformer.transform(coverageMap); + + assert.deepEqual( + mapped.fileCoverageFor('dummyFile.ts').data, + switchBranchCoverageDataExpectedResult + ); + }); });