From ab0fcddfaf4f07decc602ed1723dfe566aadcbd8 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Sun, 28 Apr 2019 23:49:42 -0700 Subject: [PATCH] feat: initial support for source-maps (#19) BREAKING CHANGE: v8-to-istanbul is now async, making it possible to use the latest source-map library --- README.md | 12 +- index.js | 4 +- lib/branch.js | 10 +- lib/function.js | 10 +- lib/line.js | 16 +- lib/pathutils.js | 19 ++ lib/script.js | 154 ---------- lib/source.js | 169 +++++++++++ lib/v8-to-istanbul.js | 187 ++++++++++++ package-lock.json | 69 ++++- package.json | 4 + ....js => test-v8-to-istanbul.js-TAP.test.js} | 275 ++++++++++++++++-- test/fixtures/scripts/branches.covered.js | 8 + test/fixtures/source-map-minified.js | 82 ++++++ test/source.js | 36 +++ test/utils/run-fixture.js | 5 +- test/{script.js => v8-to-istanbul.js} | 22 +- 17 files changed, 853 insertions(+), 229 deletions(-) create mode 100644 lib/pathutils.js delete mode 100644 lib/script.js create mode 100644 lib/source.js create mode 100644 lib/v8-to-istanbul.js rename tap-snapshots/{test-script.js-TAP.test.js => test-v8-to-istanbul.js-TAP.test.js} (70%) create mode 100644 test/fixtures/scripts/branches.covered.js create mode 100644 test/fixtures/source-map-minified.js create mode 100644 test/source.js rename test/{script.js => v8-to-istanbul.js} (60%) diff --git a/README.md b/README.md index bf3377e7..231fef79 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,12 @@ converts from v8 coverage format to [istanbul's coverage format](https://github. ```js const v8toIstanbul = require('v8-to-istanbul') -// create a script object from a source-file. the source file -// is loaded from disk and this is used to determine the original -// line count. -const script = v8toIstanbul('./path-to-instrumented-file.js') +// the path to the original source-file is required, as its contents are +// used during the conversion algorithm. +const converter = v8toIstanbul('./path-to-instrumented-file.js') +await converter.load() // this is required due to the async source-map dependency. // provide an array of coverage information in v8 format. -script.applyCoverage([ +converter.applyCoverage([ { "functionName": "", "ranges": [ @@ -31,7 +31,7 @@ script.applyCoverage([ ]) // output coverage information in a form that can // be consumed by Istanbul. -console.info(JSON.stringify(script.toIstanbul())) +console.info(JSON.stringify(converter.toIstanbul())) ``` ## Testing diff --git a/index.js b/index.js index 168ebc13..43aa9cb7 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ -const Script = require('./lib/script') +const V8ToIstanbul = require('./lib/v8-to-istanbul') module.exports = function (path, wrapperLength) { - return new Script(path, wrapperLength) + return new V8ToIstanbul(path, wrapperLength) } diff --git a/lib/branch.js b/lib/branch.js index 99c40f03..3b07cb6d 100644 --- a/lib/branch.js +++ b/lib/branch.js @@ -9,17 +9,17 @@ module.exports = class CovBranch { toIstanbul () { const location = { start: { - line: this.startLine.line, - column: this.startCol - this.startLine.startCol + line: this.startLine, + column: this.startCol }, end: { - line: this.endLine.line, - column: this.endCol - this.endLine.startCol + line: this.endLine, + column: this.endCol } } return { type: 'branch', - line: this.line, + line: this.startLine, loc: location, locations: [Object.assign({}, location)] } diff --git a/lib/function.js b/lib/function.js index 40226fb1..5259dffe 100644 --- a/lib/function.js +++ b/lib/function.js @@ -10,19 +10,19 @@ module.exports = class CovFunction { toIstanbul () { const loc = { start: { - line: this.startLine.line, - column: this.startCol - this.startLine.startCol + line: this.startLine, + column: this.startCol }, end: { - line: this.endLine.line, - column: this.endCol - this.endLine.startCol + line: this.endLine, + column: this.endCol } } return { name: this.name, decl: loc, loc: loc, - line: this.startLine.line + line: this.startLine } } } diff --git a/lib/line.js b/lib/line.js index d4e3fb5f..2313b337 100644 --- a/lib/line.js +++ b/lib/line.js @@ -1,9 +1,19 @@ module.exports = class CovLine { - constructor (line, startCol, endCol) { + constructor (line, startCol, lineStr) { this.line = line + // note that startCol and endCol are absolute positions + // within a file, not relative to the line. this.startCol = startCol - this.endCol = endCol - this.count = 0 + + // the line length itself does not include the newline characters, + // these are however taken into account when enumerating absolute offset. + const matchedNewLineChar = lineStr.match(/\r?\n$/u) + const newLineLength = matchedNewLineChar ? matchedNewLineChar[0].length : 0 + this.endCol = startCol + lineStr.length - newLineLength + + // we start with all lines having been executed, and work + // backwards zeroing out lines based on V8 output. + this.count = 1 } toIstanbul () { return { diff --git a/lib/pathutils.js b/lib/pathutils.js new file mode 100644 index 00000000..d0763fc0 --- /dev/null +++ b/lib/pathutils.js @@ -0,0 +1,19 @@ +/* + Copyright 2015, Yahoo Inc. + Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. + */ + +// pulled from: https://github.com/istanbuljs/istanbuljs +'use strict' + +const path = require('path') + +module.exports = { + isAbsolute: path.isAbsolute, + relativeTo (file, origFile) { + const p = path.isAbsolute(file) + ? file + : path.resolve(path.dirname(origFile), file) + return path.relative(process.cwd(), p) + } +} diff --git a/lib/script.js b/lib/script.js deleted file mode 100644 index 49b8beed..00000000 --- a/lib/script.js +++ /dev/null @@ -1,154 +0,0 @@ -const assert = require('assert') -const fs = require('fs') -const CovBranch = require('./branch') -const CovLine = require('./line') -const CovFunction = require('./function') - -const isNode10 = !!process.version.match(/^v10/) - -// Injected when Node.js is loading script into isolate pre Node 11. -// see: https://github.com/nodejs/node/pull/21573. -const cjsWrapperLength = isNode10 ? require('module').wrapper[0].length : 0 - -module.exports = class CovScript { - constructor (scriptPath, wrapperLength) { - assert(typeof scriptPath === 'string', 'scriptPath must be a string') - const path = parsePath(scriptPath) - this.path = path - this.source = fs.readFileSync(path, 'utf8') - this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength - const shebangLength = this._getShebangLength() - this.wrapperLength -= shebangLength - this.lines = [] - this.branches = [] - this.functions = [] - this.eof = -1 - this._buildLines(this.lines, shebangLength) - } - _buildLines (lines, shebangLength) { - let position = 0 - for (const [i, lineStr] of this.source.trim().split(/(?<=\r?\n)/u).entries()) { - const matchedNewLineChar = lineStr.match(/\r?\n$/u) - const newLineLength = matchedNewLineChar ? matchedNewLineChar[0].length : 0 - this.eof = position + lineStr.length - newLineLength - const line = new CovLine(i + 1, position, this.eof) - if (i === 0 && shebangLength !== 0) line.count = 1 - lines.push(line) - position += lineStr.length - } - } - applyCoverage (blocks) { - blocks.forEach(block => { - block.ranges.forEach((range, i) => { - const startCol = Math.max(0, range.startOffset - this.wrapperLength) - const endCol = Math.min(this.eof, range.endOffset - this.wrapperLength) - const lines = this.lines.filter(line => { - return startCol <= line.endCol && endCol >= line.startCol - }) - - if (block.isBlockCoverage && lines.length) { - // record branches. - this.branches.push(new CovBranch( - lines[0], - startCol, - lines[lines.length - 1], - endCol, - range.count - )) - - // 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( - block.functionName, - lines[0], - startCol, - lines[lines.length - 1], - endCol, - range.count - )) - } - } else if (block.functionName && lines.length) { - // record functions. - this.functions.push(new CovFunction( - block.functionName, - lines[0], - startCol, - lines[lines.length - 1], - endCol, - range.count - )) - } - - // record the lines (we record these as statements, such that we're - // compatible with Istanbul 2.0). - lines.forEach(line => { - // make sure branch spans entire line; don't record 'goodbye' - // branch in `const foo = true ? 'hello' : 'goodbye'` as a - // 0 for line coverage. - if (startCol <= line.startCol && endCol >= line.endCol) { - line.count = range.count - } - }) - }) - }) - } - toIstanbul () { - const istanbulInner = Object.assign( - { path: this.path }, - this._statementsToIstanbul(), - this._branchesToIstanbul(), - this._functionsToIstanbul() - ) - const istanbulOuter = {} - istanbulOuter[this.path] = istanbulInner - return istanbulOuter - } - _statementsToIstanbul () { - const statements = { - statementMap: {}, - s: {} - } - this.lines.forEach((line, index) => { - statements.statementMap[`${index}`] = line.toIstanbul() - statements.s[`${index}`] = line.count - }) - return statements - } - _branchesToIstanbul () { - const branches = { - branchMap: {}, - b: {} - } - this.branches.forEach((branch, index) => { - branches.branchMap[`${index}`] = branch.toIstanbul() - branches.b[`${index}`] = [branch.count] - }) - return branches - } - _functionsToIstanbul () { - const functions = { - fnMap: {}, - f: {} - } - this.functions.forEach((fn, index) => { - functions.fnMap[`${index}`] = fn.toIstanbul() - functions.f[`${index}`] = fn.count - }) - return functions - } - _getShebangLength () { - if (this.source.indexOf('#!') === 0) { - const match = this.source.match(/(?#!.*)/) - if (match) { - return match.groups.shebang.length - } - } else { - return 0 - } - } -} - -function parsePath (scriptPath) { - return scriptPath.replace('file://', '') -} diff --git a/lib/source.js b/lib/source.js new file mode 100644 index 00000000..2e527dba --- /dev/null +++ b/lib/source.js @@ -0,0 +1,169 @@ +const CovLine = require('./line') +const { GREATEST_LOWER_BOUND, LEAST_UPPER_BOUND } = require('source-map').SourceMapConsumer + +module.exports = class CovSource { + constructor (sourceRaw, wrapperLength) { + sourceRaw = sourceRaw.trimEnd() + this.lines = [] + this.eof = sourceRaw.length + this.shebangLength = getShebangLength(sourceRaw) + this.wrapperLength = wrapperLength - this.shebangLength + this._buildLines(sourceRaw) + } + _buildLines (source) { + let position = 0 + for (const [i, lineStr] of source.split(/(?<=\r?\n)/u).entries()) { + this.lines.push(new CovLine(i + 1, position, lineStr)) + position += lineStr.length + } + } + // given a start column and end column in absolute offsets within + // a source file (0 - EOF), returns the relative line column positions. + offsetToOriginalRelative (sourceMap, startCol, endCol) { + const lines = this.lines.filter((line, i) => { + return startCol <= line.endCol && endCol >= line.startCol + }) + if (!lines.length) return {} + + const start = originalPositionTryBoth( + sourceMap, + lines[0].line, + startCol - lines[0].startCol + ) + let end = originalEndPositionFor( + sourceMap, + lines[lines.length - 1].line, + endCol - lines[lines.length - 1].startCol + ) + + if (!(start && end)) { + return {} + } + + if (!(start.source && end.source)) { + return {} + } + + if (start.source !== end.source) { + return {} + } + + if (start.line === end.line && start.column === end.column) { + end = sourceMap.originalPositionFor({ + line: lines[lines.length - 1].line, + column: endCol - lines[lines.length - 1].startCol, + bias: LEAST_UPPER_BOUND + }) + end.column -= 1 + } + + return { + startLine: start.line, + relStartCol: start.column, + endLine: end.line, + relEndCol: end.column + } + } + relativeToOffset (line, relCol) { + line = Math.max(line, 1) + if (this.lines[line - 1] === undefined) return this.eof + return Math.min(this.lines[line - 1].startCol + relCol, this.lines[line - 1].endCol) + } +} + +// this implementation is pulled over from istanbul-lib-sourcemap: +// https://github.com/istanbuljs/istanbuljs/blob/master/packages/istanbul-lib-source-maps/lib/get-mapping.js +// +/** + * AST ranges are inclusive for start positions and exclusive for end positions. + * Source maps are also logically ranges over text, though interacting with + * them is generally achieved by working with explicit positions. + * + * When finding the _end_ location of an AST item, the range behavior is + * important because what we're asking for is the _end_ of whatever range + * corresponds to the end location we seek. + * + * This boils down to the following steps, conceptually, though the source-map + * library doesn't expose primitives to do this nicely: + * + * 1. Find the range on the generated file that ends at, or exclusively + * contains the end position of the AST node. + * 2. Find the range on the original file that corresponds to + * that generated range. + * 3. Find the _end_ location of that original range. + */ +function originalEndPositionFor (sourceMap, line, column) { + // Given the generated location, find the original location of the mapping + // that corresponds to a range on the generated file that overlaps the + // generated file end location. Note however that this position on its + // own is not useful because it is the position of the _start_ of the range + // on the original file, and we want the _end_ of the range. + const beforeEndMapping = originalPositionTryBoth( + sourceMap, + line, + column - 1 + ) + + if (beforeEndMapping.source === null) { + return null + } + + // Convert that original position back to a generated one, with a bump + // to the right, and a rightward bias. Since 'generatedPositionFor' searches + // for mappings in the original-order sorted list, this will find the + // mapping that corresponds to the one immediately after the + // beforeEndMapping mapping. + const afterEndMapping = sourceMap.generatedPositionFor({ + source: beforeEndMapping.source, + line: beforeEndMapping.line, + column: beforeEndMapping.column + 1, + bias: LEAST_UPPER_BOUND + }) + if ( + // If this is null, it means that we've hit the end of the file, + // so we can use Infinity as the end column. + afterEndMapping.line === null || + // If these don't match, it means that the call to + // 'generatedPositionFor' didn't find any other original mappings on + // the line we gave, so consider the binding to extend to infinity. + sourceMap.originalPositionFor(afterEndMapping).line !== + beforeEndMapping.line + ) { + return { + source: beforeEndMapping.source, + line: beforeEndMapping.line, + column: Infinity + } + } + + // Convert the end mapping into the real original position. + return sourceMap.originalPositionFor(afterEndMapping) +} + +function originalPositionTryBoth (sourceMap, line, column) { + const original = sourceMap.originalPositionFor({ + line, + column, + bias: GREATEST_LOWER_BOUND + }) + if (original.line === null) { + return sourceMap.originalPositionFor({ + line, + column, + bias: LEAST_UPPER_BOUND + }) + } else { + return original + } +} + +function getShebangLength (source) { + if (source.indexOf('#!') === 0) { + const match = source.match(/(?#!.*)/) + if (match) { + return match.groups.shebang.length + } + } else { + return 0 + } +} diff --git a/lib/v8-to-istanbul.js b/lib/v8-to-istanbul.js new file mode 100644 index 00000000..7fcebbcb --- /dev/null +++ b/lib/v8-to-istanbul.js @@ -0,0 +1,187 @@ +const { relativeTo } = require('./pathutils') +const assert = require('assert') +const convertSourceMap = require('convert-source-map') +const { dirname } = require('path') +const CovBranch = require('./branch') +const CovFunction = require('./function') +const CovSource = require('./source') +const { readFileSync } = require('fs') +const { SourceMapConsumer } = require('source-map') + +const isNode10 = !!process.version.match(/^v10/) + +// Injected when Node.js is loading script into isolate pre Node 11. +// see: https://github.com/nodejs/node/pull/21573. +const cjsWrapperLength = isNode10 ? require('module').wrapper[0].length : 0 + +module.exports = class V8ToIstanbul { + constructor (scriptPath, wrapperLength) { + assert(typeof scriptPath === 'string', 'scriptPath must be a string') + this.path = parsePath(scriptPath) + this.wrapperLength = wrapperLength === undefined ? cjsWrapperLength : wrapperLength + this.generatedLines = [] + this.branches = [] + this.functions = [] + this.sourceMap = undefined + this.source = undefined + this.sourceTranspiled = undefined + } + async load () { + const rawSource = readFileSync(this.path, 'utf8') + // if we find a source-map (either inline, or a .map file) we load + // both the transpiled and original source, both of which are used during + // the backflips we perform to remap absolute to relative positions. + const rawSourceMap = convertSourceMap.fromSource(rawSource) || convertSourceMap.fromMapFileSource(rawSource, dirname(this.path)) + 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) + } else { + this.path = relativeTo(rawSourceMap.sourcemap.sources[0], this.path) + this.sourceMap = await new SourceMapConsumer(rawSourceMap.sourcemap) + + const originalRawSource = readFileSync(this.path, 'utf8') + this.source = new CovSource(originalRawSource, this.wrapperLength) + this.sourceTranspiled = new CovSource(rawSource, this.wrapperLength) + } + } else { + this.source = new CovSource(rawSource, this.wrapperLength) + } + } + applyCoverage (blocks) { + blocks.forEach(block => { + block.ranges.forEach((range, i) => { + const { + startCol, + endCol + } = this._maybeRemapStartColEndCol(range) + + const lines = this.source.lines.filter(line => { + return startCol <= line.endCol && endCol >= line.startCol + }) + const startLineInstance = lines[0] + const endLineInstance = lines[lines.length - 1] + + if (block.isBlockCoverage && lines.length) { + // record branches. + this.branches.push(new CovBranch( + startLineInstance.line, + startCol - startLineInstance.startCol, + endLineInstance.line, + endCol - endLineInstance.startCol, + range.count + )) + + // 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( + block.functionName, + startLineInstance.line, + startCol - startLineInstance.startCol, + endLineInstance.line, + endCol - endLineInstance.startCol, + range.count + )) + } + } else if (block.functionName && lines.length) { + // record functions. + this.functions.push(new CovFunction( + block.functionName, + startLineInstance.line, + startCol - startLineInstance.startCol, + endLineInstance.line, + endCol - endLineInstance.startCol, + range.count + )) + } + + // record the lines (we record these as statements, such that we're + // compatible with Istanbul 2.0). + lines.forEach(line => { + // make sure branch spans entire line; don't record 'goodbye' + // branch in `const foo = true ? 'hello' : 'goodbye'` as a + // 0 for line coverage. + if (startCol <= line.startCol && endCol >= line.endCol) { + line.count = range.count + } else { + // console.info(`${line.line} count: ${range.count}`) + } + }) + }) + }) + } + _maybeRemapStartColEndCol (range) { + let startCol = Math.max(0, range.startOffset - this.source.wrapperLength) + let endCol = Math.min(this.source.eof, range.endOffset - this.source.wrapperLength) + + 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( + this.sourceMap, + startCol, + endCol + ) + + // 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) + } + + return { + startCol, + endCol + } + } + toIstanbul () { + const istanbulInner = Object.assign( + { path: this.path }, + this._statementsToIstanbul(), + this._branchesToIstanbul(), + this._functionsToIstanbul() + ) + const istanbulOuter = {} + istanbulOuter[this.path] = istanbulInner + return istanbulOuter + } + _statementsToIstanbul () { + const statements = { + statementMap: {}, + s: {} + } + this.source.lines.forEach((line, index) => { + statements.statementMap[`${index}`] = line.toIstanbul() + statements.s[`${index}`] = line.count + }) + return statements + } + _branchesToIstanbul () { + const branches = { + branchMap: {}, + b: {} + } + this.branches.forEach((branch, index) => { + branches.branchMap[`${index}`] = branch.toIstanbul() + branches.b[`${index}`] = [branch.count] + }) + return branches + } + _functionsToIstanbul () { + const functions = { + fnMap: {}, + f: {} + } + this.functions.forEach((fn, index) => { + functions.fnMap[`${index}`] = fn.toIstanbul() + functions.f[`${index}`] = fn.count + }) + return functions + } +} + +function parsePath (scriptPath) { + return scriptPath.replace('file://', '') +} diff --git a/package-lock.json b/package-lock.json index c2dcc0a5..0bfdba42 100644 --- a/package-lock.json +++ b/package-lock.json @@ -318,7 +318,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { @@ -964,7 +964,6 @@ "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==", - "dev": true, "requires": { "safe-buffer": "~5.1.1" } @@ -991,7 +990,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -1715,7 +1714,7 @@ }, "external-editor": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", "dev": true, "requires": { @@ -1991,7 +1990,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -2225,6 +2224,14 @@ "optimist": "^0.6.1", "source-map": "^0.6.1", "uglify-js": "^3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "har-schema": { @@ -2599,6 +2606,12 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", "integrity": "sha512-LXTBICkMARVgo579kWDm8SqfB6nvSDKNqIOBEjmJRnL04JvoMHCYGWaMddQnseJYtkEuEvO/sIcOxPLk9gERug==", "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -2909,7 +2922,7 @@ }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true }, @@ -2956,6 +2969,14 @@ "dev": true, "requires": { "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "mime-db": { @@ -3024,7 +3045,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -3172,7 +3193,7 @@ }, "resolve-from": { "version": "4.0.0", - "resolved": false, + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, @@ -3871,8 +3892,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", @@ -3977,9 +3997,9 @@ } }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", "dev": true }, "source-map-support": { @@ -3990,6 +4010,14 @@ "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "spawn-wrap": { @@ -4128,7 +4156,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -4229,7 +4257,7 @@ }, "table": { "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { @@ -4521,6 +4549,15 @@ "requires": { "commander": "~2.20.0", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, "unicode-length": { @@ -4633,7 +4670,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/package.json b/package.json index 32a3e28f..1592b64e 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,15 @@ "c8": "^3.5.0", "coveralls": "^3.0.3", "should": "^13.2.3", + "source-map": "^0.7.3", "standard": "^12.0.1", "standard-version": "^5.0.2", "tap": "^12.6.2" }, "engines": { "node": ">=10.10.0" + }, + "dependencies": { + "convert-source-map": "^1.6.0" } } diff --git a/tap-snapshots/test-script.js-TAP.test.js b/tap-snapshots/test-v8-to-istanbul.js-TAP.test.js similarity index 70% rename from tap-snapshots/test-script.js-TAP.test.js rename to tap-snapshots/test-v8-to-istanbul.js-TAP.test.js index 820a1c84..7b4922a0 100644 --- a/tap-snapshots/test-script.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/script.js TAP > undefined 1`] = ` +exports[`test/v8-to-istanbul.js TAP > undefined 1`] = ` { path: './test/fixtures/scripts/branches.js', statementMap: { '0': @@ -118,42 +118,42 @@ exports[`test/script.js TAP > undefined 1`] = ` branchMap: { '0': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 0 }, end: { line: 31, column: 10 } }, locations: [ { start: { line: 1, column: 0 }, end: { line: 31, column: 10 } } ] }, '1': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 1 }, end: { line: 31, column: 10 } }, locations: [ { start: { line: 1, column: 1 }, end: { line: 31, column: 10 } } ] }, '2': { type: 'branch', - line: undefined, + line: 5, loc: { start: { line: 5, column: 23 }, end: { line: 5, column: 28 } }, locations: [ { start: { line: 5, column: 23 }, end: { line: 5, column: 28 } } ] }, '3': { type: 'branch', - line: undefined, + line: 8, loc: { start: { line: 8, column: 22 }, end: { line: 8, column: 29 } }, locations: [ { start: { line: 8, column: 22 }, end: { line: 8, column: 29 } } ] }, '4': { type: 'branch', - line: undefined, + line: 14, loc: { start: { line: 14, column: 8 }, end: { line: 15, column: 4 } }, locations: [ { start: { line: 14, column: 8 }, end: { line: 15, column: 4 } } ] }, '5': { type: 'branch', - line: undefined, + line: 18, loc: { start: { line: 18, column: 3 }, end: { line: 18, column: 21 } }, @@ -162,14 +162,14 @@ exports[`test/script.js TAP > undefined 1`] = ` end: { line: 18, column: 21 } } ] }, '6': { type: 'branch', - line: undefined, + line: 22, loc: { start: { line: 22, column: 8 }, end: { line: 30, column: 8 } }, locations: [ { start: { line: 22, column: 8 }, end: { line: 30, column: 8 } } ] }, '7': { type: 'branch', - line: undefined, + line: 29, loc: { start: { line: 29, column: 31 }, end: { line: 30, column: 6 } }, @@ -196,7 +196,7 @@ exports[`test/script.js TAP > undefined 1`] = ` f: { '0': 1 } } ` -exports[`test/script.js TAP > undefined 2`] = ` +exports[`test/v8-to-istanbul.js TAP > undefined 2`] = ` { path: './test/fixtures/scripts/functions.js', statementMap: { '0': @@ -368,21 +368,21 @@ exports[`test/script.js TAP > undefined 2`] = ` branchMap: { '0': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 0 }, end: { line: 48, column: 9 } }, locations: [ { start: { line: 1, column: 0 }, end: { line: 48, column: 9 } } ] }, '1': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 1 }, end: { line: 48, column: 9 } }, locations: [ { start: { line: 1, column: 1 }, end: { line: 48, column: 9 } } ] }, '2': { type: 'branch', - line: undefined, + line: 11, loc: { start: { line: 11, column: 8 }, end: { line: 19, column: 13 } }, @@ -391,14 +391,14 @@ exports[`test/script.js TAP > undefined 2`] = ` end: { line: 19, column: 13 } } ] }, '3': { type: 'branch', - line: undefined, + line: 13, loc: { start: { line: 13, column: 6 }, end: { line: 14, column: 1 } }, locations: [ { start: { line: 13, column: 6 }, end: { line: 14, column: 1 } } ] }, '4': { type: 'branch', - line: undefined, + line: 18, loc: { start: { line: 18, column: 32 }, end: { line: 18, column: 37 } }, @@ -407,7 +407,7 @@ exports[`test/script.js TAP > undefined 2`] = ` end: { line: 18, column: 37 } } ] }, '5': { type: 'branch', - line: undefined, + line: 19, loc: { start: { line: 19, column: 9 }, end: { line: 19, column: 12 } }, @@ -416,14 +416,14 @@ exports[`test/script.js TAP > undefined 2`] = ` end: { line: 19, column: 12 } } ] }, '6': { type: 'branch', - line: undefined, + line: 27, loc: { start: { line: 27, column: 1 }, end: { line: 28, column: 1 } }, locations: [ { start: { line: 27, column: 1 }, end: { line: 28, column: 1 } } ] }, '7': { type: 'branch', - line: undefined, + line: 43, loc: { start: { line: 43, column: 15 }, end: { line: 44, column: 5 } }, @@ -432,7 +432,7 @@ exports[`test/script.js TAP > undefined 2`] = ` end: { line: 44, column: 5 } } ] }, '8': { type: 'branch', - line: undefined, + line: 47, loc: { start: { line: 47, column: 5 }, end: { line: 48, column: 9 } }, locations: @@ -510,7 +510,7 @@ exports[`test/script.js TAP > undefined 2`] = ` f: { '0': 0, '1': 1, '2': 2, '3': 0, '4': 0, '5': 1, '6': 1 } } ` -exports[`test/script.js TAP > undefined 3`] = ` +exports[`test/v8-to-istanbul.js TAP > undefined 3`] = ` { path: './test/fixtures/scripts/mixed-newlines.js', statementMap: { '0': @@ -523,14 +523,14 @@ exports[`test/script.js TAP > undefined 3`] = ` branchMap: { '0': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 0 }, end: { line: 3, column: 1 } }, locations: [ { start: { line: 1, column: 0 }, end: { line: 3, column: 1 } } ] }, '1': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 1 }, end: { line: 3, column: 1 } }, locations: @@ -540,7 +540,7 @@ exports[`test/script.js TAP > undefined 3`] = ` f: {} } ` -exports[`test/script.js TAP > undefined 4`] = ` +exports[`test/v8-to-istanbul.js TAP > undefined 4`] = ` { path: './test/fixtures/scripts/shebang.js', statementMap: { '0': @@ -564,14 +564,14 @@ exports[`test/script.js TAP > undefined 4`] = ` branchMap: { '0': { type: 'branch', - line: undefined, + line: 1, loc: { start: { line: 1, column: 19 }, end: { line: 8, column: 7 } }, locations: [ { start: { line: 1, column: 19 }, end: { line: 8, column: 7 } } ] }, '1': { type: 'branch', - line: undefined, + line: 2, loc: { start: { line: 2, column: 0 }, end: { line: 8, column: 7 } }, locations: @@ -580,3 +580,228 @@ exports[`test/script.js TAP > undefined 4`] = ` fnMap: {}, f: {} } ` + +exports[`test/v8-to-istanbul.js TAP > undefined 5`] = ` +{ path: 'test/fixtures/scripts/branches.js', + statementMap: + { '0': + { start: { line: 1, column: 0 }, end: { line: 1, column: 26 } }, + '1': + { start: { line: 2, column: 0 }, end: { line: 2, column: 18 } }, + '2': + { start: { line: 3, column: 0 }, end: { line: 3, column: 0 } }, + '3': + { start: { line: 4, column: 0 }, end: { line: 4, column: 31 } }, + '4': + { start: { line: 5, column: 0 }, end: { line: 5, column: 37 } }, + '5': + { start: { line: 6, column: 0 }, end: { line: 6, column: 0 } }, + '6': + { start: { line: 7, column: 0 }, end: { line: 7, column: 18 } }, + '7': + { start: { line: 8, column: 0 }, end: { line: 8, column: 31 } }, + '8': + { start: { line: 9, column: 0 }, end: { line: 9, column: 0 } }, + '9': + { start: { line: 10, column: 0 }, + end: { line: 10, column: 16 } }, + '10': + { start: { line: 11, column: 0 }, + end: { line: 11, column: 12 } }, + '11': + { start: { line: 12, column: 0 }, + end: { line: 12, column: 14 } }, + '12': + { start: { line: 13, column: 0 }, end: { line: 13, column: 1 } }, + '13': + { start: { line: 14, column: 0 }, end: { line: 14, column: 8 } }, + '14': + { start: { line: 15, column: 0 }, + end: { line: 15, column: 25 } }, + '15': + { start: { line: 16, column: 0 }, end: { line: 16, column: 3 } }, + '16': + { start: { line: 17, column: 0 }, end: { line: 17, column: 0 } }, + '17': + { start: { line: 18, column: 0 }, + end: { line: 18, column: 28 } }, + '18': + { start: { line: 19, column: 0 }, + end: { line: 19, column: 15 } }, + '19': + { start: { line: 20, column: 0 }, + end: { line: 20, column: 13 } }, + '20': + { start: { line: 21, column: 0 }, + end: { line: 21, column: 23 } }, + '21': + { start: { line: 22, column: 0 }, + end: { line: 22, column: 10 } }, + '22': + { start: { line: 23, column: 0 }, + end: { line: 23, column: 16 } }, + '23': + { start: { line: 24, column: 0 }, end: { line: 24, column: 3 } }, + '24': + { start: { line: 25, column: 0 }, end: { line: 25, column: 1 } }, + '25': + { start: { line: 26, column: 0 }, end: { line: 26, column: 0 } }, + '26': + { start: { line: 27, column: 0 }, end: { line: 27, column: 3 } }, + '27': + { start: { line: 28, column: 0 }, end: { line: 28, column: 0 } }, + '28': + { start: { line: 29, column: 0 }, + end: { line: 29, column: 46 } }, + '29': + { start: { line: 30, column: 0 }, + end: { line: 30, column: 15 } }, + '30': + { start: { line: 31, column: 0 }, + end: { line: 31, column: 10 } } }, + s: + { '0': 0, + '1': 1, + '2': 1, + '3': 1, + '4': 1, + '5': 1, + '6': 1, + '7': 1, + '8': 1, + '9': 1, + '10': 1, + '11': 1, + '12': 1, + '13': 1, + '14': 0, + '15': 0, + '16': 0, + '17': 0, + '18': 1, + '19': 1, + '20': 1, + '21': 1, + '22': 1, + '23': 1, + '24': 1, + '25': 1, + '26': 1, + '27': 1, + '28': 0, + '29': 1, + '30': 1 }, + branchMap: + { '0': + { type: 'branch', + line: 31, + loc: + { start: { line: 31, column: 10 }, + end: { line: 31, column: 10 } }, + locations: + [ { start: { line: 31, column: 10 }, + end: { line: 31, column: 10 } } ] }, + '1': + { type: 'branch', + line: 5, + loc: + { start: { line: 5, column: 7 }, end: { line: 5, column: 18 } }, + locations: + [ { start: { line: 5, column: 7 }, end: { line: 5, column: 18 } } ] }, + '2': + { type: 'branch', + line: 8, + loc: + { start: { line: 8, column: 0 }, end: { line: 8, column: 11 } }, + locations: + [ { start: { line: 8, column: 0 }, end: { line: 8, column: 11 } } ] }, + '3': + { type: 'branch', + line: 11, + loc: + { start: { line: 11, column: 11 }, + end: { line: 12, column: 12 } }, + locations: + [ { start: { line: 11, column: 11 }, + end: { line: 12, column: 12 } } ] }, + '4': + { type: 'branch', + line: 14, + loc: + { start: { line: 14, column: 7 }, + end: { line: 18, column: 28 } }, + locations: + [ { start: { line: 14, column: 7 }, + end: { line: 18, column: 28 } } ] }, + '5': + { type: 'branch', + line: 31, + loc: + { start: { line: 31, column: 10 }, + end: { line: 31, column: 10 } }, + locations: + [ { start: { line: 31, column: 10 }, + end: { line: 31, column: 10 } } ] }, + '6': + { type: 'branch', + line: 1, + loc: + { start: { line: 1, column: 0 }, end: { line: 2, column: 16 } }, + locations: + [ { start: { line: 1, column: 0 }, end: { line: 2, column: 16 } } ] }, + '7': + { type: 'branch', + line: 1, + loc: + { start: { line: 1, column: 0 }, end: { line: 2, column: 6 } }, + locations: + [ { start: { line: 1, column: 0 }, end: { line: 2, column: 6 } } ] }, + '8': + { type: 'branch', + line: 2, + loc: + { start: { line: 2, column: 0 }, end: { line: 2, column: 10 } }, + locations: + [ { start: { line: 2, column: 0 }, end: { line: 2, column: 10 } } ] }, + '9': + { type: 'branch', + line: 20, + loc: + { start: { line: 20, column: 12 }, + end: { line: 30, column: 10 } }, + locations: + [ { start: { line: 20, column: 12 }, + end: { line: 30, column: 10 } } ] }, + '10': + { type: 'branch', + line: 29, + loc: + { start: { line: 29, column: 0 }, + end: { line: 30, column: 10 } }, + locations: + [ { start: { line: 29, column: 0 }, + end: { line: 30, column: 10 } } ] } }, + b: + { '0': [ 1 ], + '1': [ 0 ], + '2': [ 0 ], + '3': [ 0 ], + '4': [ 0 ], + '5': [ 0 ], + '6': [ 1 ], + '7': [ 0 ], + '8': [ 0 ], + '9': [ 1 ], + '10': [ 0 ] }, + fnMap: + { '0': + { name: 'e', + decl: + { start: { line: 20, column: 12 }, + end: { line: 30, column: 10 } }, + loc: + { start: { line: 20, column: 12 }, + end: { line: 30, column: 10 } }, + line: 20 } }, + f: { '0': 1 } } +` diff --git a/test/fixtures/scripts/branches.covered.js b/test/fixtures/scripts/branches.covered.js new file mode 100644 index 00000000..aec6c062 --- /dev/null +++ b/test/fixtures/scripts/branches.covered.js @@ -0,0 +1,8 @@ +var cov_1myqaytex8=function(){var path="./test/fixtures/scripts/branches.js";var hash="05fd1c5758177cf8534c86e9665d000812274ffa";var global=new Function("return this")();var gcv="__coverage__";var coverageData={path:"./test/fixtures/scripts/branches.js",statementMap:{"0":{start:{line:2,column:10},end:{line:2,column:18}},"1":{start:{line:5,column:10},end:{line:5,column:37}},"2":{start:{line:8,column:10},end:{line:8,column:31}},"3":{start:{line:11,column:0},end:{line:16,column:3}},"4":{start:{line:12,column:12},end:{line:12,column:14}},"5":{start:{line:15,column:4},end:{line:15,column:25}},"6":{start:{line:20,column:2},end:{line:24,column:3}},"7":{start:{line:21,column:4},end:{line:21,column:23}},"8":{start:{line:23,column:14},end:{line:23,column:16}},"9":{start:{line:27,column:0},end:{line:27,column:3}},"10":{start:{line:30,column:10},end:{line:31,column:10}}},fnMap:{"0":{name:"e",decl:{start:{line:19,column:9},end:{line:19,column:10}},loc:{start:{line:19,column:14},end:{line:25,column:1}},line:19}},branchMap:{"0":{loc:{start:{line:2,column:10},end:{line:2,column:18}},type:"binary-expr",locations:[{start:{line:2,column:10},end:{line:2,column:12}},{start:{line:2,column:16},end:{line:2,column:18}}],line:2},"1":{loc:{start:{line:5,column:10},end:{line:5,column:37}},type:"cond-expr",locations:[{start:{line:5,column:18},end:{line:5,column:25}},{start:{line:5,column:28},end:{line:5,column:37}}],line:5},"2":{loc:{start:{line:8,column:10},end:{line:8,column:31}},type:"binary-expr",locations:[{start:{line:8,column:10},end:{line:8,column:11}},{start:{line:8,column:15},end:{line:8,column:16}},{start:{line:8,column:20},end:{line:8,column:25}},{start:{line:8,column:29},end:{line:8,column:31}}],line:8},"3":{loc:{start:{line:11,column:0},end:{line:16,column:3}},type:"if",locations:[{start:{line:11,column:0},end:{line:16,column:3}},{start:{line:11,column:0},end:{line:16,column:3}}],line:11},"4":{loc:{start:{line:20,column:2},end:{line:24,column:3}},type:"if",locations:[{start:{line:20,column:2},end:{line:24,column:3}},{start:{line:20,column:2},end:{line:24,column:3}}],line:20},"5":{loc:{start:{line:30,column:10},end:{line:31,column:10}},type:"binary-expr",locations:[{start:{line:30,column:10},end:{line:30,column:12}},{start:{line:31,column:2},end:{line:31,column:4}},{start:{line:31,column:8},end:{line:31,column:10}}],line:30}},s:{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0},f:{"0":0},b:{"0":[0,0],"1":[0,0],"2":[0,0,0,0],"3":[0,0],"4":[0,0],"5":[0,0,0]},_coverageSchema:"43e27e138ebf9cfc5966b082cf9a028302ed4184",hash:"05fd1c5758177cf8534c86e9665d000812274ffa"};var coverage=global[gcv]||(global[gcv]={});if(coverage[path]&&coverage[path].hash===hash){return coverage[path];}return coverage[path]=coverageData;}();// basic binary operation. +const a=(cov_1myqaytex8.s[0]++,(cov_1myqaytex8.b[0][0]++,99)||(cov_1myqaytex8.b[0][1]++,33));// basic conditional operation. +const b=(cov_1myqaytex8.s[1]++,false?(cov_1myqaytex8.b[1][0]++,'hello'):(cov_1myqaytex8.b[1][1]++,'goodbye'));// nary operation. +const c=(cov_1myqaytex8.s[2]++,(cov_1myqaytex8.b[2][0]++,a)&&(cov_1myqaytex8.b[2][1]++,b)&&(cov_1myqaytex8.b[2][2]++,false)&&(cov_1myqaytex8.b[2][3]++,33));// if statement. +cov_1myqaytex8.s[3]++;if(false){cov_1myqaytex8.b[3][0]++;const d=(cov_1myqaytex8.s[4]++,99);}else{cov_1myqaytex8.b[3][1]++;cov_1myqaytex8.s[5]++;console.info('hello');}// if statement in function. +function e(){cov_1myqaytex8.f[0]++;cov_1myqaytex8.s[6]++;if(true){cov_1myqaytex8.b[4][0]++;cov_1myqaytex8.s[7]++;console.info('hey');}else{cov_1myqaytex8.b[4][1]++;const f=(cov_1myqaytex8.s[8]++,99);}}cov_1myqaytex8.s[9]++;e();// binary operation that spans multiple lines. +const g=(cov_1myqaytex8.s[10]++,(cov_1myqaytex8.b[5][0]++,99)&&(cov_1myqaytex8.b[5][1]++,33)||(cov_1myqaytex8.b[5][2]++,13)); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4vYnJhbmNoZXMuanMiXSwibmFtZXMiOlsiYSIsImIiLCJjIiwiZCIsImNvbnNvbGUiLCJpbmZvIiwiZSIsImYiLCJnIl0sIm1hcHBpbmdzIjoieXlGQUFBO0FBQ0EsS0FBTUEsQ0FBQUEsQ0FBQyx3QkFBRyx5REFBTSxFQUFOLENBQUgsQ0FBUCxDQUVBO0FBQ0EsS0FBTUMsQ0FBQUEsQ0FBQyx3QkFBRyxnQ0FBUSxPQUFSLDRCQUFrQixTQUFsQixDQUFILENBQVAsQ0FFQTtBQUNBLEtBQU1DLENBQUFBLENBQUMsd0JBQUcsMEJBQUFGLENBQUMsNkJBQUlDLENBQUosQ0FBRCw0QkFBVSxLQUFWLDZCQUFtQixFQUFuQixDQUFILENBQVAsQ0FFQTtzQkFDQSxHQUFJLEtBQUosQ0FBVywwQkFDVCxLQUFNRSxDQUFBQSxDQUFDLHdCQUFHLEVBQUgsQ0FBUCxDQUNELENBRkQsSUFHTyxnREFDSEMsT0FBTyxDQUFDQyxJQUFSLENBQWEsT0FBYixFQUNELENBRUg7QUFDQSxRQUFTQyxDQUFBQSxDQUFULEVBQWMsNkNBQ1osR0FBSSxJQUFKLENBQVUsZ0RBQ1JGLE9BQU8sQ0FBQ0MsSUFBUixDQUFhLEtBQWIsRUFDRCxDQUZELElBRU8sMEJBQ0wsS0FBTUUsQ0FBQUEsQ0FBQyx3QkFBRyxFQUFILENBQVAsQ0FDRCxDQUNGLEMsc0JBRURELENBQUMsR0FFRDtBQUNBLEtBQU1FLENBQUFBLENBQUMseUJBQUcseURBQ1IsRUFEUSw2QkFDRixFQURFLENBQUgsQ0FBUCIsInNvdXJjZXNDb250ZW50IjpbIi8vIGJhc2ljIGJpbmFyeSBvcGVyYXRpb24uXG5jb25zdCBhID0gOTkgfHwgMzNcblxuLy8gYmFzaWMgY29uZGl0aW9uYWwgb3BlcmF0aW9uLlxuY29uc3QgYiA9IGZhbHNlID8gJ2hlbGxvJyA6ICdnb29kYnllJ1xuXG4vLyBuYXJ5IG9wZXJhdGlvbi5cbmNvbnN0IGMgPSBhICYmIGIgJiYgZmFsc2UgJiYgMzNcblxuLy8gaWYgc3RhdGVtZW50LlxuaWYgKGZhbHNlKSB7XG4gIGNvbnN0IGQgPSA5OVxufVxuICBlbHNlIHtcbiAgICBjb25zb2xlLmluZm8oJ2hlbGxvJylcbiAgfVxuXG4vLyBpZiBzdGF0ZW1lbnQgaW4gZnVuY3Rpb24uXG5mdW5jdGlvbiBlICgpIHtcbiAgaWYgKHRydWUpIHtcbiAgICBjb25zb2xlLmluZm8oJ2hleScpXG4gIH0gZWxzZSB7XG4gICAgY29uc3QgZiA9IDk5XG4gIH1cbn1cblxuZSgpXG5cbi8vIGJpbmFyeSBvcGVyYXRpb24gdGhhdCBzcGFucyBtdWx0aXBsZSBsaW5lcy5cbmNvbnN0IGcgPSA5OSAmJlxuICAzMyB8fCAxM1xuIl19 diff --git a/test/fixtures/source-map-minified.js b/test/fixtures/source-map-minified.js new file mode 100644 index 00000000..b8ca3137 --- /dev/null +++ b/test/fixtures/source-map-minified.js @@ -0,0 +1,82 @@ +module.exports = { + describe: 'source-map and minified source', + coverageV8: { + "scriptId": "57", + "url": "./test/fixtures/scripts/branches.covered.js", + "functions": [ + { + "functionName": "", + "ranges": [ + { + "startOffset": 0, + "endOffset": 3907, + "count": 1 + }, + { + "startOffset": 2944, + "endOffset": 2975, + "count": 0 + }, + { + "startOffset": 3045, + "endOffset": 3080, + "count": 0 + }, + { + "startOffset": 3261, + "endOffset": 3292, + "count": 0 + }, + { + "startOffset": 3342, + "endOffset": 3404, + "count": 0 + }, + { + "startOffset": 3874, + "endOffset": 3905, + "count": 0 + } + ], + "isBlockCoverage": true + }, + { + "functionName": "", + "ranges": [ + { + "startOffset": 19, + "endOffset": 2854, + "count": 1 + }, + { + "startOffset": 2765, + "endOffset": 2793, + "count": 0 + }, + { + "startOffset": 2794, + "endOffset": 2818, + "count": 0 + } + ], + "isBlockCoverage": true + }, + { + "functionName": "e", + "ranges": [ + { + "startOffset": 3508, + "endOffset": 3709, + "count": 1 + }, + { + "startOffset": 3642, + "endOffset": 3708, + "count": 0 + } + ], + "isBlockCoverage": true + } + ] + } +} \ No newline at end of file diff --git a/test/source.js b/test/source.js new file mode 100644 index 00000000..5f2910d5 --- /dev/null +++ b/test/source.js @@ -0,0 +1,36 @@ +/* global describe, it */ + +const CovSource = require('../lib/source') + +require('tap').mochaGlobals() +require('should') + +describe('Source', () => { + describe('relativeToOffset', () => { + it('returns an offset in the middle of a file', () => { + const sourceRaw = `const a = 99 + const b = 33 + for (var i = 0; i < 99; i++) { + console.info('hello world') + } + return a` + const source = new CovSource(sourceRaw, 0) + source.relativeToOffset(3, 8).should.equal(42) + }) + + it('returns EOF if line is greater than lines in file', () => { + const sourceRaw = ` + const a = 99` + const source = new CovSource(sourceRaw, 0) + source.relativeToOffset(5, 8).should.equal(21) + }) + + it('returns end of line, if column offset greater than line length', () => { + const sourceRaw = `a + const a = 99` + const source = new CovSource(sourceRaw, 0) + source.relativeToOffset(2, 50).should.equal(22) + source.relativeToOffset(1, Infinity).should.equal(1) + }) + }) +}) diff --git a/test/utils/run-fixture.js b/test/utils/run-fixture.js index 4266d3f0..7dbc9d14 100644 --- a/test/utils/run-fixture.js +++ b/test/utils/run-fixture.js @@ -6,8 +6,9 @@ const t = require('tap') t.mochaGlobals() require('should') -module.exports = (fixture) => { +module.exports = async (fixture) => { const script = toIstanbul(fixture.coverageV8.url) + await script.load() script.applyCoverage(fixture.coverageV8.functions) let coverageIstanbul = script.toIstanbul() @@ -16,8 +17,6 @@ module.exports = (fixture) => { coverageIstanbul = coverageIstanbul[Object.keys(coverageIstanbul)[0]] describe(fixture.describe, () => { - // run with DEBUG=true to output coverage information to - // terminal; this is useful when writing new tests. it('matches snapshot', () => { t.matchSnapshot(coverageIstanbul) }) diff --git a/test/script.js b/test/v8-to-istanbul.js similarity index 60% rename from test/script.js rename to test/v8-to-istanbul.js index 02890084..58614390 100644 --- a/test/script.js +++ b/test/v8-to-istanbul.js @@ -3,28 +3,30 @@ const { readdirSync, lstatSync } = require('fs') const path = require('path') const runFixture = require('./utils/run-fixture') -const Script = require('../lib/script') +const V8ToIstanbul = require('../lib/v8-to-istanbul') require('tap').mochaGlobals() require('should') -describe('Script', () => { +describe('V8ToIstanbul', () => { describe('constructor', () => { - it('creates line instance for each line in script', () => { - const script = new Script( + it('creates line instance for each line in V8ToIstanbul', async () => { + const v8ToIstanbul = new V8ToIstanbul( require.resolve('./fixtures/scripts/functions.js') ) - script.lines.length.should.equal(48) - script.wrapperLength.should.equal(0) // common-js header. + await v8ToIstanbul.load() + v8ToIstanbul.source.lines.length.should.equal(48) + v8ToIstanbul.wrapperLength.should.equal(0) // common-js header. }) - it('handles ESM style paths', () => { - const script = new Script( + it('handles ESM style paths', async () => { + const v8ToIstanbul = new V8ToIstanbul( `file://${require.resolve('./fixtures/scripts/functions.js')}`, 0 ) - script.lines.length.should.equal(48) - script.wrapperLength.should.equal(0) // ESM header. + await v8ToIstanbul.load() + v8ToIstanbul.source.lines.length.should.equal(48) + v8ToIstanbul.wrapperLength.should.equal(0) // ESM header. }) })