diff --git a/.gitignore b/.gitignore index da8673f3..69e8c4be 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ coverage .nyc_output node_modules .idea +.vscode diff --git a/index.d.ts b/index.d.ts index 1ab7c62e..726c9f0d 100644 --- a/index.d.ts +++ b/index.d.ts @@ -19,6 +19,6 @@ declare class V8ToIstanbul { toIstanbul(): CoverageMapData } -declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources): V8ToIstanbul +declare function v8ToIstanbul(scriptPath: string, wrapperLength?: number, sources?: Sources, excludePath?: (path) => boolean): V8ToIstanbul export = v8ToIstanbul diff --git a/index.js b/index.js index e7ec9f98..4db27a7d 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ const V8ToIstanbul = require('./lib/v8-to-istanbul') -module.exports = function (path, wrapperLength, sources) { - return new V8ToIstanbul(path, wrapperLength, sources) +module.exports = function (path, wrapperLength, sources, excludePath) { + return new V8ToIstanbul(path, wrapperLength, sources, excludePath) } diff --git a/lib/source.js b/lib/source.js index 60569da2..9ccd22a9 100644 --- a/lib/source.js +++ b/lib/source.js @@ -86,6 +86,7 @@ module.exports = class CovSource { } return { + source: start.source, startLine: start.line, relStartCol: start.column, endLine: end.line, diff --git a/lib/v8-to-istanbul.js b/lib/v8-to-istanbul.js index d1cd6f46..d6af42e9 100644 --- a/lib/v8-to-istanbul.js +++ b/lib/v8-to-istanbul.js @@ -20,17 +20,18 @@ const isNode8 = /^v8\./.test(process.version) const cjsWrapperLength = isOlderNode10 ? require('module').wrapper[0].length : 0 module.exports = class V8ToIstanbul { - constructor (scriptPath, wrapperLength, sources) { + constructor (scriptPath, wrapperLength, sources, excludePath) { assert(typeof scriptPath === 'string', 'scriptPath must be a string') assert(!isNode8, 'This module does not support node 8 or lower, please upgrade to node 10') this.path = parsePath(scriptPath) this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength + this.excludePath = excludePath || (() => false) this.sources = sources || {} this.generatedLines = [] - this.branches = [] - this.functions = [] + this.branches = {} + this.functions = {} + this.covSources = [] this.sourceMap = undefined - this.source = undefined this.sourceTranspiled = undefined } @@ -44,8 +45,9 @@ module.exports = class V8ToIstanbul { if (rawSourceMap) { if (rawSourceMap.sourcemap.sources.length > 1) { - console.warn('v8-to-istanbul: source-mappings from one to many files not yet supported') - this.source = new CovSource(rawSource, this.wrapperLength) + this.sourceMap = await new SourceMapConsumer(rawSourceMap.sourcemap) + this.covSources = this.sourceMap.sourcesContent.map((rawSource, i) => ({ source: new CovSource(rawSource, this.wrapperLength), path: this.sourceMap.sources[i] })) + this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) } else { this._rewritePath(rawSourceMap) this.sourceMap = await new SourceMapConsumer(rawSourceMap.sourcemap) @@ -63,11 +65,11 @@ module.exports = class V8ToIstanbul { originalRawSource = await readFile(this.path, 'utf8') } - this.source = new CovSource(originalRawSource, this.wrapperLength) + this.covSources = [{ source: new CovSource(originalRawSource, this.wrapperLength), path: this.sourceMap.sources[0] }] this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) } } else { - this.source = new CovSource(rawSource, this.wrapperLength) + this.covSources = [{ source: new CovSource(rawSource, this.wrapperLength), path: this.path }] } } @@ -86,11 +88,11 @@ module.exports = class V8ToIstanbul { applyCoverage (blocks) { blocks.forEach(block => { block.ranges.forEach((range, i) => { - const { - startCol, - endCol - } = this._maybeRemapStartColEndCol(range) - const lines = this.source.lines.filter(line => { + const { startCol, endCol, path, covSource } = this._maybeRemapStartColEndCol(range) + if (this.excludePath(path)) { + return + } + const lines = covSource.lines.filter(line => { // Upstream tooling can provide a block with the functionName // (empty-report), this will result in a report that has all // lines zeroed out. @@ -98,14 +100,16 @@ module.exports = class V8ToIstanbul { line.count = 0 return true } + return startCol <= line.endCol && endCol >= line.startCol }) const startLineInstance = lines[0] const endLineInstance = lines[lines.length - 1] if (block.isBlockCoverage && lines.length) { + this.branches[path] = this.branches[path] || [] // record branches. - this.branches.push(new CovBranch( + this.branches[path].push(new CovBranch( startLineInstance.line, startCol - startLineInstance.startCol, endLineInstance.line, @@ -116,7 +120,8 @@ module.exports = class V8ToIstanbul { // if block-level granularity is enabled, we we still create a single // CovFunction tracking object for each set of ranges. if (block.functionName && i === 0) { - this.functions.push(new CovFunction( + this.functions[path] = this.functions[path] || [] + this.functions[path].push(new CovFunction( block.functionName, startLineInstance.line, startCol - startLineInstance.startCol, @@ -126,8 +131,9 @@ module.exports = class V8ToIstanbul { )) } } else if (block.functionName && lines.length) { + this.functions[path] = this.functions[path] || [] // record functions. - this.functions.push(new CovFunction( + this.functions[path].push(new CovFunction( block.functionName, startLineInstance.line, startCol - startLineInstance.startCol, @@ -148,6 +154,7 @@ module.exports = class V8ToIstanbul { // if they are not invoked; line.ignore prevents a line from being // set to 0, and is set if the special comment /* c8 ignore next */ // is used. + if (startCol <= line.startCol && endCol >= line.endCol && !line.ignore) { line.count = range.count } @@ -157,75 +164,92 @@ module.exports = class V8ToIstanbul { } _maybeRemapStartColEndCol (range) { - let startCol = Math.max(0, range.startOffset - this.source.wrapperLength) - let endCol = Math.min(this.source.eof, range.endOffset - this.source.wrapperLength) + let covSource = this.covSources[0].source + let startCol = Math.max(0, range.startOffset - covSource.wrapperLength) + let endCol = Math.min(covSource.eof, range.endOffset - covSource.wrapperLength) + let path = this.path if (this.sourceMap) { startCol = Math.max(0, range.startOffset - this.sourceTranspiled.wrapperLength) endCol = Math.min(this.sourceTranspiled.eof, range.endOffset - this.sourceTranspiled.wrapperLength) - const { startLine, relStartCol, endLine, relEndCol } = this.sourceTranspiled.offsetToOriginalRelative( + const { startLine, relStartCol, endLine, relEndCol, source } = this.sourceTranspiled.offsetToOriginalRelative( this.sourceMap, startCol, endCol ) + const matchingSource = this.covSources.find(covSource => covSource.path === source) + covSource = matchingSource ? matchingSource.source : this.covSources[0].source + path = matchingSource ? matchingSource.path : this.covSources[0].path; + // next we convert these relative positions back to absolute positions // in the original source (which is the format expected in the next step). - startCol = this.source.relativeToOffset(startLine, relStartCol) - endCol = this.source.relativeToOffset(endLine, relEndCol) + startCol = covSource.relativeToOffset(startLine, relStartCol) + endCol = covSource.relativeToOffset(endLine, relEndCol) } return { + path, + covSource, startCol, endCol } } + getInnerIstanbul (source, path) { + if (this.excludePath(path)) { + return + } + + return { + [path]: { + path, + ...this._statementsToIstanbul(source, path), + ...this._branchesToIstanbul(source, path), + ...this._functionsToIstanbul(source, path) + } + } + } + toIstanbul () { - const istanbulInner = Object.assign( - { path: this.path }, - this._statementsToIstanbul(), - this._branchesToIstanbul(), - this._functionsToIstanbul() - ) - const istanbulOuter = {} - istanbulOuter[this.path] = istanbulInner - return istanbulOuter + return this.covSources.reduce((istanbulOuter, { source, path }) => Object.assign(istanbulOuter, this.getInnerIstanbul(source, path)), {}) } - _statementsToIstanbul () { + _statementsToIstanbul (source, path) { const statements = { statementMap: {}, s: {} } - this.source.lines.forEach((line, index) => { + source.lines.forEach((line, index) => { statements.statementMap[`${index}`] = line.toIstanbul() statements.s[`${index}`] = line.count }) return statements } - _branchesToIstanbul () { + _branchesToIstanbul (source, path) { const branches = { branchMap: {}, b: {} } - this.branches.forEach((branch, index) => { - const ignore = this.source.lines[branch.startLine - 1].ignore + this.branches[path] = this.branches[path] || [] + this.branches[path].forEach((branch, index) => { + const ignore = source.lines[branch.startLine - 1].ignore branches.branchMap[`${index}`] = branch.toIstanbul() branches.b[`${index}`] = [ignore ? 1 : branch.count] }) return branches } - _functionsToIstanbul () { + _functionsToIstanbul (source, path) { const functions = { fnMap: {}, f: {} } - this.functions.forEach((fn, index) => { - const ignore = this.source.lines[fn.startLine - 1].ignore + this.functions[path] = this.functions[path] || [] + this.functions[path].forEach((fn, index) => { + const ignore = source.lines[fn.startLine - 1].ignore functions.fnMap[`${index}`] = fn.toIstanbul() functions.f[`${index}`] = ignore ? 1 : fn.count }) diff --git a/tap-snapshots/test-v8-to-istanbul.js-TAP.test.js b/tap-snapshots/test-v8-to-istanbul.js-TAP.test.js index fc99c607..71b50fcb 100644 --- a/tap-snapshots/test-v8-to-istanbul.js-TAP.test.js +++ b/tap-snapshots/test-v8-to-istanbul.js-TAP.test.js @@ -5,7 +5,7 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports['test/v8-to-istanbul.js TAP > must match snapshot 1'] = ` +exports[`test/v8-to-istanbul.js TAP > must match branches snapshot 1`] = ` Object { "b": Object { "0": Array [ @@ -620,7 +620,7 @@ Object { } ` -exports['test/v8-to-istanbul.js TAP > must match snapshot 2'] = ` +exports[`test/v8-to-istanbul.js TAP > must match functions snapshot 1`] = ` Object { "b": Object { "0": Array [ @@ -1601,7 +1601,7 @@ Object { } ` -exports['test/v8-to-istanbul.js TAP > must match snapshot 3'] = ` +exports[`test/v8-to-istanbul.js TAP > must match mixed new lines snapshot 1`] = ` Object { "b": Object { "0": Array [ @@ -1707,7 +1707,7 @@ Object { } ` -exports['test/v8-to-istanbul.js TAP > must match snapshot 4'] = ` +exports[`test/v8-to-istanbul.js TAP > must match shebang snapshot 1`] = ` Object { "b": Object { "0": Array [ @@ -1868,7 +1868,7 @@ Object { } ` -exports['test/v8-to-istanbul.js TAP > must match snapshot 5'] = ` +exports[`test/v8-to-istanbul.js TAP > must match source-map and minified source snapshot 1`] = ` Object { "b": Object { "0": Array [ @@ -2569,3 +2569,1081 @@ Object { }, } ` + +exports[`test/v8-to-istanbul.js TAP > must match source-map with multiple source files snapshot 1`] = ` +Object { + "b": Object {}, + "branchMap": Object {}, + "f": Object { + "0": 2, + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + }, + "fnMap": Object { + "0": Object { + "decl": Object { + "end": Object { + "column": 25, + "line": 25, + }, + "start": Object { + "column": 2, + "line": 5, + }, + }, + "line": 5, + "loc": Object { + "end": Object { + "column": 25, + "line": 25, + }, + "start": Object { + "column": 2, + "line": 5, + }, + }, + "name": "n", + }, + "1": Object { + "decl": Object { + "end": Object { + "column": 76, + "line": 38, + }, + "start": Object { + "column": 26, + "line": 36, + }, + }, + "line": 36, + "loc": Object { + "end": Object { + "column": 76, + "line": 38, + }, + "start": Object { + "column": 26, + "line": 36, + }, + }, + "name": "n.d", + }, + "2": Object { + "decl": Object { + "end": Object { + "column": 65, + "line": 47, + }, + "start": Object { + "column": 26, + "line": 43, + }, + }, + "line": 43, + "loc": Object { + "end": Object { + "column": 65, + "line": 47, + }, + "start": Object { + "column": 26, + "line": 43, + }, + }, + "name": "n.r", + }, + "3": Object { + "decl": Object { + "end": Object { + "column": 13, + "line": 63, + }, + "start": Object { + "column": 26, + "line": 55, + }, + }, + "line": 55, + "loc": Object { + "end": Object { + "column": 13, + "line": 63, + }, + "start": Object { + "column": 26, + "line": 55, + }, + }, + "name": "n.t", + }, + "4": Object { + "decl": Object { + "end": Object { + "column": 17, + "line": 72, + }, + "start": Object { + "column": 26, + "line": 67, + }, + }, + "line": 67, + "loc": Object { + "end": Object { + "column": 17, + "line": 72, + }, + "start": Object { + "column": 26, + "line": 67, + }, + }, + "name": "n.n", + }, + "5": Object { + "decl": Object { + "end": Object { + "column": 120, + "line": 76, + }, + "start": Object { + "column": 26, + "line": 76, + }, + }, + "line": 76, + "loc": Object { + "end": Object { + "column": 120, + "line": 76, + }, + "start": Object { + "column": 26, + "line": 76, + }, + }, + "name": "n.o", + }, + }, + "s": Object { + "0": 1, + "1": 1, + "10": 2, + "11": 2, + "12": 2, + "13": 2, + "14": 2, + "15": 2, + "16": 2, + "17": 2, + "18": 2, + "19": 2, + "2": 1, + "20": 2, + "21": 2, + "22": 2, + "23": 2, + "24": 2, + "25": 1, + "26": 1, + "27": 1, + "28": 1, + "29": 1, + "3": 1, + "30": 1, + "31": 1, + "32": 1, + "33": 1, + "34": 1, + "35": 1, + "36": 0, + "37": 0, + "38": 1, + "39": 1, + "4": 1, + "40": 1, + "41": 1, + "42": 1, + "43": 0, + "44": 0, + "45": 0, + "46": 0, + "47": 1, + "48": 1, + "49": 1, + "5": 2, + "50": 1, + "51": 1, + "52": 1, + "53": 1, + "54": 1, + "55": 0, + "56": 0, + "57": 0, + "58": 0, + "59": 0, + "6": 2, + "60": 0, + "61": 0, + "62": 0, + "63": 1, + "64": 1, + "65": 1, + "66": 1, + "67": 0, + "68": 0, + "69": 0, + "7": 2, + "70": 0, + "71": 0, + "72": 1, + "73": 1, + "74": 1, + "75": 1, + "76": 1, + "77": 1, + "78": 1, + "79": 1, + "8": 2, + "80": 1, + "81": 1, + "82": 1, + "9": 2, + }, + "statementMap": Object { + "0": Object { + "end": Object { + "column": 21, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "1": Object { + "end": Object { + "column": 28, + "line": 2, + }, + "start": Object { + "column": 0, + "line": 2, + }, + }, + "10": Object { + "end": Object { + "column": 53, + "line": 11, + }, + "start": Object { + "column": 0, + "line": 11, + }, + }, + "11": Object { + "end": Object { + "column": 46, + "line": 12, + }, + "start": Object { + "column": 0, + "line": 12, + }, + }, + "12": Object { + "end": Object { + "column": 16, + "line": 13, + }, + "start": Object { + "column": 0, + "line": 13, + }, + }, + "13": Object { + "end": Object { + "column": 13, + "line": 14, + }, + "start": Object { + "column": 0, + "line": 14, + }, + }, + "14": Object { + "end": Object { + "column": 15, + "line": 15, + }, + "start": Object { + "column": 0, + "line": 15, + }, + }, + "15": Object { + "end": Object { + "column": 5, + "line": 16, + }, + "start": Object { + "column": 0, + "line": 16, + }, + }, + "16": Object { + "end": Object { + "column": 0, + "line": 17, + }, + "start": Object { + "column": 0, + "line": 17, + }, + }, + "17": Object { + "end": Object { + "column": 33, + "line": 18, + }, + "start": Object { + "column": 0, + "line": 18, + }, + }, + "18": Object { + "end": Object { + "column": 87, + "line": 19, + }, + "start": Object { + "column": 0, + "line": 19, + }, + }, + "19": Object { + "end": Object { + "column": 0, + "line": 20, + }, + "start": Object { + "column": 0, + "line": 20, + }, + }, + "2": Object { + "end": Object { + "column": 0, + "line": 3, + }, + "start": Object { + "column": 0, + "line": 3, + }, + }, + "20": Object { + "end": Object { + "column": 31, + "line": 21, + }, + "start": Object { + "column": 0, + "line": 21, + }, + }, + "21": Object { + "end": Object { + "column": 19, + "line": 22, + }, + "start": Object { + "column": 0, + "line": 22, + }, + }, + "22": Object { + "end": Object { + "column": 0, + "line": 23, + }, + "start": Object { + "column": 0, + "line": 23, + }, + }, + "23": Object { + "end": Object { + "column": 38, + "line": 24, + }, + "start": Object { + "column": 0, + "line": 24, + }, + }, + "24": Object { + "end": Object { + "column": 25, + "line": 25, + }, + "start": Object { + "column": 0, + "line": 25, + }, + }, + "25": Object { + "end": Object { + "column": 3, + "line": 26, + }, + "start": Object { + "column": 0, + "line": 26, + }, + }, + "26": Object { + "end": Object { + "column": 0, + "line": 27, + }, + "start": Object { + "column": 0, + "line": 27, + }, + }, + "27": Object { + "end": Object { + "column": 0, + "line": 28, + }, + "start": Object { + "column": 0, + "line": 28, + }, + }, + "28": Object { + "end": Object { + "column": 52, + "line": 29, + }, + "start": Object { + "column": 0, + "line": 29, + }, + }, + "29": Object { + "end": Object { + "column": 34, + "line": 30, + }, + "start": Object { + "column": 0, + "line": 30, + }, + }, + "3": Object { + "end": Object { + "column": 25, + "line": 4, + }, + "start": Object { + "column": 0, + "line": 4, + }, + }, + "30": Object { + "end": Object { + "column": 0, + "line": 31, + }, + "start": Object { + "column": 0, + "line": 31, + }, + }, + "31": Object { + "end": Object { + "column": 28, + "line": 32, + }, + "start": Object { + "column": 0, + "line": 32, + }, + }, + "32": Object { + "end": Object { + "column": 43, + "line": 33, + }, + "start": Object { + "column": 0, + "line": 33, + }, + }, + "33": Object { + "end": Object { + "column": 0, + "line": 34, + }, + "start": Object { + "column": 0, + "line": 34, + }, + }, + "34": Object { + "end": Object { + "column": 47, + "line": 35, + }, + "start": Object { + "column": 0, + "line": 35, + }, + }, + "35": Object { + "end": Object { + "column": 59, + "line": 36, + }, + "start": Object { + "column": 0, + "line": 36, + }, + }, + "36": Object { + "end": Object { + "column": 46, + "line": 37, + }, + "start": Object { + "column": 0, + "line": 37, + }, + }, + "37": Object { + "end": Object { + "column": 76, + "line": 38, + }, + "start": Object { + "column": 0, + "line": 38, + }, + }, + "38": Object { + "end": Object { + "column": 4, + "line": 39, + }, + "start": Object { + "column": 0, + "line": 39, + }, + }, + "39": Object { + "end": Object { + "column": 4, + "line": 40, + }, + "start": Object { + "column": 0, + "line": 40, + }, + }, + "4": Object { + "end": Object { + "column": 42, + "line": 5, + }, + "start": Object { + "column": 0, + "line": 5, + }, + }, + "40": Object { + "end": Object { + "column": 0, + "line": 41, + }, + "start": Object { + "column": 0, + "line": 41, + }, + }, + "41": Object { + "end": Object { + "column": 33, + "line": 42, + }, + "start": Object { + "column": 0, + "line": 42, + }, + }, + "42": Object { + "end": Object { + "column": 45, + "line": 43, + }, + "start": Object { + "column": 0, + "line": 43, + }, + }, + "43": Object { + "end": Object { + "column": 60, + "line": 44, + }, + "start": Object { + "column": 0, + "line": 44, + }, + }, + "44": Object { + "end": Object { + "column": 76, + "line": 45, + }, + "start": Object { + "column": 0, + "line": 45, + }, + }, + "45": Object { + "end": Object { + "column": 4, + "line": 46, + }, + "start": Object { + "column": 0, + "line": 46, + }, + }, + "46": Object { + "end": Object { + "column": 65, + "line": 47, + }, + "start": Object { + "column": 0, + "line": 47, + }, + }, + "47": Object { + "end": Object { + "column": 4, + "line": 48, + }, + "start": Object { + "column": 0, + "line": 48, + }, + }, + "48": Object { + "end": Object { + "column": 0, + "line": 49, + }, + "start": Object { + "column": 0, + "line": 49, + }, + }, + "49": Object { + "end": Object { + "column": 35, + "line": 50, + }, + "start": Object { + "column": 0, + "line": 50, + }, + }, + "5": Object { + "end": Object { + "column": 0, + "line": 6, + }, + "start": Object { + "column": 0, + "line": 6, + }, + }, + "50": Object { + "end": Object { + "column": 47, + "line": 51, + }, + "start": Object { + "column": 0, + "line": 51, + }, + }, + "51": Object { + "end": Object { + "column": 56, + "line": 52, + }, + "start": Object { + "column": 0, + "line": 52, + }, + }, + "52": Object { + "end": Object { + "column": 50, + "line": 53, + }, + "start": Object { + "column": 0, + "line": 53, + }, + }, + "53": Object { + "end": Object { + "column": 36, + "line": 54, + }, + "start": Object { + "column": 0, + "line": 54, + }, + }, + "54": Object { + "end": Object { + "column": 49, + "line": 55, + }, + "start": Object { + "column": 0, + "line": 55, + }, + }, + "55": Object { + "end": Object { + "column": 51, + "line": 56, + }, + "start": Object { + "column": 0, + "line": 56, + }, + }, + "56": Object { + "end": Object { + "column": 29, + "line": 57, + }, + "start": Object { + "column": 0, + "line": 57, + }, + }, + "57": Object { + "end": Object { + "column": 89, + "line": 58, + }, + "start": Object { + "column": 0, + "line": 58, + }, + }, + "58": Object { + "end": Object { + "column": 32, + "line": 59, + }, + "start": Object { + "column": 0, + "line": 59, + }, + }, + "59": Object { + "end": Object { + "column": 29, + "line": 60, + }, + "start": Object { + "column": 0, + "line": 60, + }, + }, + "6": Object { + "end": Object { + "column": 33, + "line": 7, + }, + "start": Object { + "column": 0, + "line": 7, + }, + }, + "60": Object { + "end": Object { + "column": 76, + "line": 61, + }, + "start": Object { + "column": 0, + "line": 61, + }, + }, + "61": Object { + "end": Object { + "column": 151, + "line": 62, + }, + "start": Object { + "column": 0, + "line": 62, + }, + }, + "62": Object { + "end": Object { + "column": 13, + "line": 63, + }, + "start": Object { + "column": 0, + "line": 63, + }, + }, + "63": Object { + "end": Object { + "column": 4, + "line": 64, + }, + "start": Object { + "column": 0, + "line": 64, + }, + }, + "64": Object { + "end": Object { + "column": 0, + "line": 65, + }, + "start": Object { + "column": 0, + "line": 65, + }, + }, + "65": Object { + "end": Object { + "column": 73, + "line": 66, + }, + "start": Object { + "column": 0, + "line": 66, + }, + }, + "66": Object { + "end": Object { + "column": 44, + "line": 67, + }, + "start": Object { + "column": 0, + "line": 67, + }, + }, + "67": Object { + "end": Object { + "column": 45, + "line": 68, + }, + "start": Object { + "column": 0, + "line": 68, + }, + }, + "68": Object { + "end": Object { + "column": 57, + "line": 69, + }, + "start": Object { + "column": 0, + "line": 69, + }, + }, + "69": Object { + "end": Object { + "column": 51, + "line": 70, + }, + "start": Object { + "column": 0, + "line": 70, + }, + }, + "7": Object { + "end": Object { + "column": 35, + "line": 8, + }, + "start": Object { + "column": 0, + "line": 8, + }, + }, + "70": Object { + "end": Object { + "column": 46, + "line": 71, + }, + "start": Object { + "column": 0, + "line": 71, + }, + }, + "71": Object { + "end": Object { + "column": 17, + "line": 72, + }, + "start": Object { + "column": 0, + "line": 72, + }, + }, + "72": Object { + "end": Object { + "column": 4, + "line": 73, + }, + "start": Object { + "column": 0, + "line": 73, + }, + }, + "73": Object { + "end": Object { + "column": 0, + "line": 74, + }, + "start": Object { + "column": 0, + "line": 74, + }, + }, + "74": Object { + "end": Object { + "column": 41, + "line": 75, + }, + "start": Object { + "column": 0, + "line": 75, + }, + }, + "75": Object { + "end": Object { + "column": 120, + "line": 76, + }, + "start": Object { + "column": 0, + "line": 76, + }, + }, + "76": Object { + "end": Object { + "column": 0, + "line": 77, + }, + "start": Object { + "column": 0, + "line": 77, + }, + }, + "77": Object { + "end": Object { + "column": 28, + "line": 78, + }, + "start": Object { + "column": 0, + "line": 78, + }, + }, + "78": Object { + "end": Object { + "column": 29, + "line": 79, + }, + "start": Object { + "column": 0, + "line": 79, + }, + }, + "79": Object { + "end": Object { + "column": 0, + "line": 80, + }, + "start": Object { + "column": 0, + "line": 80, + }, + }, + "8": Object { + "end": Object { + "column": 46, + "line": 9, + }, + "start": Object { + "column": 0, + "line": 9, + }, + }, + "80": Object { + "end": Object { + "column": 0, + "line": 81, + }, + "start": Object { + "column": 0, + "line": 81, + }, + }, + "81": Object { + "end": Object { + "column": 41, + "line": 82, + }, + "start": Object { + "column": 0, + "line": 82, + }, + }, + "82": Object { + "end": Object { + "column": 56, + "line": 83, + }, + "start": Object { + "column": 0, + "line": 83, + }, + }, + "9": Object { + "end": Object { + "column": 4, + "line": 10, + }, + "start": Object { + "column": 0, + "line": 10, + }, + }, + }, +} +` diff --git a/test/fixtures/mixed-newlines.js b/test/fixtures/mixed-newlines.js index e80956f9..c7d3f896 100644 --- a/test/fixtures/mixed-newlines.js +++ b/test/fixtures/mixed-newlines.js @@ -1,5 +1,5 @@ module.exports = { - describe: 'functions', + describe: 'mixed new lines', coverageV8: { "scriptId": "71", "url": "./test/fixtures/scripts/mixed-newlines.js", diff --git a/test/fixtures/multi-source-map.js b/test/fixtures/multi-source-map.js new file mode 100755 index 00000000..b4aeb779 --- /dev/null +++ b/test/fixtures/multi-source-map.js @@ -0,0 +1,163 @@ +module.exports = { + describe: 'source-map with multiple source files', + coverageV8: { + "scriptId": "57", + "url": "./test/fixtures/scripts/sourcemap-multisource.js", + "functions": [ + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1658, + "startOffset": 0 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 910, + "startOffset": 1 + } + ] + }, + { + "functionName": "n", + "isBlockCoverage": false, + "ranges": [ + { + "count": 2, + "endOffset": 156, + "startOffset": 22 + } + ] + }, + { + "functionName": "n.d", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 247, + "startOffset": 172 + } + ] + }, + { + "functionName": "n.r", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 422, + "startOffset": 252 + } + ] + }, + { + "functionName": "n.t", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 714, + "startOffset": 427 + } + ] + }, + { + "functionName": "n.n", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 825, + "startOffset": 719 + } + ] + }, + { + "functionName": "n.o", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 893, + "startOffset": 830 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1485, + "startOffset": 912 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1182, + "startOffset": 1152 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1353, + "startOffset": 1297 + } + ] + }, + { + "functionName": "t.counter", + "isBlockCoverage": true, + "ranges": [ + { + "count": 1, + "endOffset": 1454, + "startOffset": 1395 + } + ] + }, + { + "functionName": "t.setCounter", + "isBlockCoverage": false, + "ranges": [ + { + "count": 0, + "endOffset": 1484, + "startOffset": 1468 + } + ] + }, + { + "functionName": "", + "isBlockCoverage": false, + "ranges": [ + { + "count": 1, + "endOffset": 1605, + "startOffset": 1486 + } + ] + } + ] + } +} \ No newline at end of file diff --git a/test/fixtures/scripts/sourcemap-multisource.js b/test/fixtures/scripts/sourcemap-multisource.js new file mode 100644 index 00000000..50474eb2 --- /dev/null +++ b/test/fixtures/scripts/sourcemap-multisource.js @@ -0,0 +1,2 @@ +!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.setCounter=t.counter=void 0;var r=n(1);console.log(r.HELLO_WORLD);var o=0,u=document.createElement("button");u.innerHTML="click me",u.addEventListener("click",(function(){return t.counter()})),document.body.appendChild(u);var c=document.createElement("input");c.type="number",c.addEventListener("change",(function(e){return t.setCounter(Number(e.target.value))})),document.body.appendChild(c),t.counter=function(){-5!==o&&(console.log(o++),c.value=o.toString())},t.setCounter=function(e){o=e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.HELLO_WORLD=void 0,t.HELLO_WORLD="123"}]); +//# sourceMappingURL=sourcemap-multisource.js.map \ No newline at end of file diff --git a/test/fixtures/scripts/sourcemap-multisource.js.map b/test/fixtures/scripts/sourcemap-multisource.js.map new file mode 100644 index 00000000..7ce91b1e --- /dev/null +++ b/test/fixtures/scripts/sourcemap-multisource.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/index.ts","webpack:///./src/utils.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","console","log","HELLO_WORLD","count","button","document","createElement","innerHTML","addEventListener","counter","body","appendChild","input","type","e","setCounter","Number","target","toString"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,8GClFrD,WAEAC,QAAQC,IAAI,EAAAC,aACZ,IAAIC,EAAQ,EAENC,EAASC,SAASC,cAAc,UACtCF,EAAOG,UAAY,WACnBH,EAAOI,iBAAiB,SAAS,WAAM,SAAAC,aACvCJ,SAASK,KAAKC,YAAYP,GAG1B,IAAMQ,EAAQP,SAASC,cAAc,SACrCM,EAAMC,KAAO,SACbD,EAAMJ,iBAAiB,UAAU,SAACM,GAAY,SAAAC,WAAWC,OAAOF,EAAEG,OAAOhC,WACzEoB,SAASK,KAAKC,YAAYC,GAEb,EAAAH,QAAU,YACL,IAAXN,IAGHH,QAAQC,IAAIE,KACZS,EAAM3B,MAAQkB,EAAMe,aAGX,EAAAH,WAAa,SAAC9B,GACvBkB,EAAQlB,I,mGCzBC,EAAAiB,YAAc","file":"sourcemap-multisource.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","import { HELLO_WORLD } from './utils';\n\nconsole.log(HELLO_WORLD);\nlet count = 0;\n\nconst button = document.createElement('button');\nbutton.innerHTML = 'click me';\nbutton.addEventListener('click', () => counter());\ndocument.body.appendChild(button);\n\n\nconst input = document.createElement('input');\ninput.type = 'number';\ninput.addEventListener('change', (e : any) => setCounter(Number(e.target.value)));\ndocument.body.appendChild(input);\n\nexport const counter = () => {\n if(count === -5) {\n return\n }\n console.log(count++);\n input.value = count.toString();\n}\n\nexport const setCounter = (value: number) => {\n count = value;\n};\n","export const HELLO_WORLD = '123';"],"sourceRoot":""} \ No newline at end of file diff --git a/test/utils/run-fixture.js b/test/utils/run-fixture.js index 839ba2d8..019a6c4b 100644 --- a/test/utils/run-fixture.js +++ b/test/utils/run-fixture.js @@ -19,7 +19,7 @@ module.exports = async (fixture) => { describe(fixture.describe, () => { it('matches snapshot', () => { delete coverageIstanbul.path - t.matchSnapshot(coverageIstanbul) + t.matchSnapshot(coverageIstanbul, `must match ${fixture.describe} snapshot`) }) }) } diff --git a/test/v8-to-istanbul.js b/test/v8-to-istanbul.js index 100f47f7..dbda9c4a 100644 --- a/test/v8-to-istanbul.js +++ b/test/v8-to-istanbul.js @@ -18,7 +18,8 @@ describe('V8ToIstanbul', async () => { require.resolve('./fixtures/scripts/functions.js') ) await v8ToIstanbul.load() - v8ToIstanbul.source.lines.length.should.equal(48) + v8ToIstanbul.covSources[0].source.lines.length.should.equal(48) + v8ToIstanbul.covSources.length.should.equal(1) v8ToIstanbul.wrapperLength.should.equal(0) // common-js header. }) @@ -28,7 +29,8 @@ describe('V8ToIstanbul', async () => { 0 ) await v8ToIstanbul.load() - v8ToIstanbul.source.lines.length.should.equal(48) + v8ToIstanbul.covSources[0].source.lines.length.should.equal(48) + v8ToIstanbul.covSources.length.should.equal(1) v8ToIstanbul.wrapperLength.should.equal(0) // ESM header. }) @@ -119,7 +121,26 @@ ${'//'}${'#'} sourceMappingURL=data:application/json;base64,${base64Sourcemap} }] }]) }) + + it('should exclude files when passing excludePath', async () => { + const v8ToIstanbul = new V8ToIstanbul( + `file://${require.resolve('./fixtures/scripts/sourcemap-multisource.js')}`, + 0, + undefined, + path => path.indexOf('bootstrap') > -1 + ) + await v8ToIstanbul.load() + v8ToIstanbul.applyCoverage([{ + functionName: 'fake', + ranges: [{ + startOffset: 0, + endOffset: 1 + }] + }]) + Object.keys(v8ToIstanbul.toIstanbul()).should.eql(['webpack:///src/index.ts', 'webpack:///src/utils.ts']) + }) }) + describe('source map format edge cases', () => { let consoleWarn beforeEach(() => { @@ -145,6 +166,17 @@ ${'//'}${'#'} sourceMappingURL=data:application/json;base64,${base64Sourcemap} await v8ToIstanbul.load() assert(v8ToIstanbul.path.includes('v8-to-istanbul/test/fixtures/one-up/relative-source-root.js')) }) + + it('should handles source maps with moultiple sources', async () => { + const v8ToIstanbul = new V8ToIstanbul( + `file://${require.resolve('./fixtures/scripts/sourcemap-multisource.js')}`, + 0 + ) + await v8ToIstanbul.load() + + v8ToIstanbul.covSources.length.should.equal(3) + Object.keys(v8ToIstanbul.toIstanbul()).should.eql(['webpack:///webpack/bootstrap', 'webpack:///src/index.ts', 'webpack:///src/utils.ts']) + }) }) // execute JavaScript files in fixtures directory; these