diff --git a/lib/output-files.js b/lib/output-files.js index c49429c..025a7aa 100644 --- a/lib/output-files.js +++ b/lib/output-files.js @@ -1,19 +1,61 @@ // output JavaScript bundled in puppeteer output to format // that can be eaten by Istanbul. + +// TODO: Put function interfaces on this file + +const fs = require('fs') +const mkdirp = require('mkdirp') +const clone = require('clone') +const pathLib = require('path') + class OutputFiles { constructor (coverageInfo) { - this.coverageInfo = coverageInfo + // Clone coverageInfo to prevent mutating the passed in data + this.coverageInfo = clone(coverageInfo) + this.iterator = 0 + this._parseAndIsolate() } + rewritePath (path) { // generate a new path relative to ./coverage/js. // this would be around where you'd use mkdirp. - return `${path}-hey!` + + var str = `` + + // Get the last element in the path name + var truncatedPath = pathLib.basename(path) + + // Special case: when html present, strip and return specialized string + if (truncatedPath.includes('.html')) { + truncatedPath = './coverage/js/puppeteerTemp-inline' + } else { + truncatedPath = truncatedPath.split('.js')[0] + truncatedPath = './coverage/js/' + truncatedPath + } + mkdirp.sync('./coverage/js') + if (fs.existsSync(truncatedPath + '.js')) { + this.iterator++ + str = `${truncatedPath}-${this.iterator}.js` + return str + } else { + str = `${truncatedPath}.js` + return str + } + } + + _parseAndIsolate () { + for (var i = 0; i < this.coverageInfo.length; i++) { + var path = this.rewritePath(this.coverageInfo[i].url) + this.coverageInfo[i].url = path + fs.writeFileSync(path, this.coverageInfo[i].text) + } } - output () { + getTransformedCoverage () { + return this.coverageInfo } } -module.exports = function (converageInfo) { - return new OutputFiles(converageInfo) +module.exports = function (coverageInfo) { + return new OutputFiles(coverageInfo) } diff --git a/package.json b/package.json index f78dd4e..fd1335f 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "homepage": "https://github.com/istanbuljs/puppeteer-to-istanbul#readme", "devDependencies": { "chai": "^4.1.2", + "clone": "^2.1.1", "coveralls": "^3.0.0", "mocha": "^5.0.1", "nyc": "^11.4.1", diff --git a/test/fixtures/function-coverage-full-duplicate.json b/test/fixtures/function-coverage-full-duplicate.json new file mode 100644 index 0000000..b97793d --- /dev/null +++ b/test/fixtures/function-coverage-full-duplicate.json @@ -0,0 +1,38 @@ +[ + { + "url": "file:///C:/istanbul-puppeteer/puppeteer-to-istanbul/test/sample_js/function-coverage-100.js", + "ranges": [ + { + "start": 0, + "end": 47 + }, + { + "start": 49, + "end": 90 + }, + { + "start": 92, + "end": 112 + } + ], + "text": "function a (num1, num2) {\r\n return num1 + num2\r\n}\r\n\r\nfunction b (num) {\r\n return num + 1\r\n}\r\n\r\na(1, 2)\r\nb(3)\r\n" + }, + { + "url": "file:///C:/istanbul-puppeteer/puppeteer-to-istanbul/test/sample_js/function-coverage-100.js", + "ranges": [ + { + "start": 0, + "end": 47 + }, + { + "start": 49, + "end": 90 + }, + { + "start": 92, + "end": 112 + } + ], + "text": "function a (num1, num2) {\r\n return num1 + num2\r\n}\r\n\r\nfunction b (num) {\r\n return num + 1\r\n}\r\n\r\na(1, 2)\r\nb(3)\r\n" + } +] diff --git a/test/fixtures/inline-and-external-script-coverage.json b/test/fixtures/inline-and-external-script-coverage.json new file mode 100644 index 0000000..9abbd8e --- /dev/null +++ b/test/fixtures/inline-and-external-script-coverage.json @@ -0,0 +1,30 @@ +[ + { + "url": "file:////home/viktor/Documents/HackIllinois2018/puppeteer-to-istanbul/test/sample_js/block-else-not-covered.js", + "ranges": [ + { + "start": 0, + "end": 44 + } + ], + "text": "if (true) {\n console.info('hello world!')\n} else {\n function fib () {\n return 'i am the fibonacci sequence'\n }\n}" + }, + { + "url": "file:///tmp/puppeteerTemp.html", + "ranges": [ + { + "start": 0, + "end": 55 + }, + { + "start": 60, + "end": 66 + }, + { + "start": 109, + "end": 126 + } + ], + "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " + } +] \ No newline at end of file diff --git a/test/fixtures/inline-script-coverage.json b/test/fixtures/inline-script-coverage.json new file mode 100644 index 0000000..5d1c509 --- /dev/null +++ b/test/fixtures/inline-script-coverage.json @@ -0,0 +1,20 @@ +[ + { + "url": "file:///tmp/puppeteerTemp.html", + "ranges": [ + { + "start": 0, + "end": 59 + }, + { + "start": 66, + "end": 74 + }, + { + "start": 121, + "end": 140 + } + ], + "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " + } +] \ No newline at end of file diff --git a/test/fixtures/two-inline.json b/test/fixtures/two-inline.json new file mode 100644 index 0000000..2d8b0f1 --- /dev/null +++ b/test/fixtures/two-inline.json @@ -0,0 +1,34 @@ +[ + { + "url": "file:///tmp/puppeteerTemp.html", + "ranges": [ + { + "start": 0, + "end": 55 + }, + { + "start": 60, + "end": 66 + }, + { + "start": 109, + "end": 126 + } + ], + "text": "\n function c(num1, num2) {\n return num2 * num1;\n }\n function d(num3) {\n return num3;\n }\n c(4,3);\n " + }, + { + "url": "file:///tmp/puppeteerTemp.html", + "ranges": [ + { + "start": 0, + "end": 68 + }, + { + "start": 73, + "end": 93 + } + ], + "text": "\n function e(num4, num5, num6) {\n return num4 * num5 - num6;\n }\n e(3,5,6);\n " + } +] \ No newline at end of file diff --git a/test/output-files.js b/test/output-files.js index 62e5cf8..a24b414 100644 --- a/test/output-files.js +++ b/test/output-files.js @@ -1,24 +1,71 @@ -/* globals describe, it */ +/* globals describe, it, beforeEach, after */ const OutputFiles = require('../lib/output-files') +const rimraf = require('rimraf') require('chai').should() -describe('puppeteer-to-v8', () => { - describe('filename generation', () => { - // we can use path.basename(path[, ext]). - it('exposes a helper for generating a filename', () => { - const outputFiles = OutputFiles() // you would pass the JSON blob in here. - console.info(outputFiles.rewritePath('./foo/bar/hey.txt')) - }) +describe('output-files', () => { + beforeEach(cleanupCoverage) - it('appropriately handles colliding names', () => { + // we can use path.basename(path[, ext]). + it('exposes a handler that appropriately handles colliding names', () => { + const outputFiles = OutputFiles(require('./fixtures/block-else-not-covered.json')) - }) + // Since block-else-not-covered was generated by the above line, this + // should make a new file with -1 appended to the name + var newPath = outputFiles.rewritePath('./sample_js/block-else-not-covered-1.js') + newPath.should.eql('./coverage/js/block-else-not-covered-1.js') + }) + + it('handle multiple files with same name, and replace in json', () => { + // Input from the fixture should be JSONified already + const fixture = require('./fixtures/function-coverage-full-duplicate.json') + const coverageInfo = OutputFiles(fixture).getTransformedCoverage() + + // Fixture should and output coverage should not be in the same place + coverageInfo[0].url.should.not.eql(fixture[0].url) + coverageInfo[1].url.should.not.eql(fixture[1].url) + + coverageInfo[0].url.should.eql(movedUrl(fixture[0].url)) + coverageInfo[1].url.should.eql(movedUrl(fixture[0].url.replace('.js', '-1.js'))) + }) + + // call it something like indexHTML-inline-1.js + it('appropriately handles only inline JavaScript', () => { + const fixture = require('./fixtures/inline-script-coverage.json') + const coverageInfo = OutputFiles(fixture).getTransformedCoverage() + + coverageInfo[0].url.should.include('puppeteerTemp-inline.js') + }) - // call it something like indexHTML-inline-1.js - it('appropriately handles inline JavaScript', () => { + it('appropriately handles inline and external JavaScript', () => { + const fixture = require('./fixtures/inline-and-external-script-coverage.json') + const coverageInfo = OutputFiles(fixture).getTransformedCoverage() - }) + coverageInfo[0].url.should.eql(movedUrl(fixture[0].url)) + coverageInfo[1].url.should.include('puppeteerTemp-inline.js') }) + + it('appropriately handles two cases of inline JavaScript', () => { + const fixture = require('./fixtures/two-inline.json') + const coverageInfo = OutputFiles(fixture).getTransformedCoverage() + + coverageInfo[0].url.should.include('puppeteerTemp-inline.js') + coverageInfo[1].url.should.include('puppeteerTemp-inline-1.js') + }) + + after(cleanupCoverage) + + function cleanupCoverage () { + rimraf.sync('./coverage') + } + + // Takes in a script and rewrites it to the path we expect in /coverage/js + function movedUrl (url) { + let splitUrl = url.split('/') + + // Prepend the folder to the filename + return './coverage/js/' + splitUrl[splitUrl.length - 1] + } }) diff --git a/test/puppeteer-to-v8.js b/test/puppeteer-to-v8.js index 22dabd1..0ce7f48 100644 --- a/test/puppeteer-to-v8.js +++ b/test/puppeteer-to-v8.js @@ -26,22 +26,10 @@ describe('puppeteer-to-v8', () => { firstV8Range.count.should.eql(1) }) - // use mkdirp: - // mkdirp.sync('./coverage/js') - it('rewrites filename to be relative to ./coverage/js', () => { - // const fixture = require('./fixtures/block-else-not-covered') - }) - // look at the uuid library: // uuid.v4() it('generates scriptId', () => { // Ensures that the scriptId is of type 'number' (typeof v8Coverage[0].scriptId).should.eql('number') }) - - // for this test case, make sure we cover what happens - // if there are multiple script tags in the same HTML file. - it('handles coverage output for inline JavaScript', () => { - - }) }) diff --git a/test/sample_js/inline.js b/test/sample_js/inline.js new file mode 100644 index 0000000..a1588d5 --- /dev/null +++ b/test/sample_js/inline.js @@ -0,0 +1,13 @@ +/* + This was included as a