diff --git a/lib/output-files.js b/lib/output-files.js index c7aef34..a40135c 100644 --- a/lib/output-files.js +++ b/lib/output-files.js @@ -7,6 +7,7 @@ const fs = require('fs') const mkdirp = require('mkdirp') const clone = require('clone') const pathLib = require('path') +const url = require('url') const storagePath = './.nyc_output/js' @@ -18,29 +19,51 @@ class OutputFiles { this._parseAndIsolate() } + parsePath (path) { + let urlPath + + try { + urlPath = new url.URL(path) + } catch (error) { + path = 'file://' + path + urlPath = new url.URL(path) + } + + let postProtocolPath = urlPath.pathname.substring(1) + + if (urlPath.hostname) { + let hostnameAndPort = urlPath.hostname + if (urlPath.port) { + hostnameAndPort = hostnameAndPort + '_' + urlPath.port + } + + postProtocolPath = hostnameAndPort + '/' + postProtocolPath + } + + return postProtocolPath + } + rewritePath (path) { // generate a new path relative to ./coverage/js. // this would be around where you'd use mkdirp. - var str = `` - - // Get the last element in the path name - var truncatedPath = pathLib.basename(path) + let str = `` + let parsedPath = this.parsePath(path) // Special case: when html present, strip and return specialized string - if (truncatedPath.includes('.html')) { - truncatedPath = pathLib.resolve(storagePath, truncatedPath) + 'puppeteerTemp-inline' + if (parsedPath.includes('.html')) { + parsedPath = pathLib.resolve(storagePath, parsedPath) + 'puppeteerTemp-inline' } else { - truncatedPath = truncatedPath.split('.js')[0] - truncatedPath = pathLib.resolve(storagePath, truncatedPath) + parsedPath = parsedPath.split('.js')[0] + parsedPath = pathLib.resolve(storagePath, parsedPath) } mkdirp.sync(storagePath) - if (fs.existsSync(truncatedPath + '.js')) { + if (fs.existsSync(parsedPath + '.js')) { this.iterator++ - str = `${truncatedPath}-${this.iterator}.js` + str = `${parsedPath}-${this.iterator}.js` return str } else { - str = `${truncatedPath}.js` + str = `${parsedPath}.js` return str } } @@ -49,6 +72,9 @@ class OutputFiles { for (var i = 0; i < this.coverageInfo.length; i++) { var path = this.rewritePath(this.coverageInfo[i].url) this.coverageInfo[i].url = path + + mkdirp.sync(pathLib.parse(path).dir) + fs.writeFileSync(path, this.coverageInfo[i].text) } } diff --git a/test/fixtures/http-es6-modules.json b/test/fixtures/http-es6-modules.json new file mode 100644 index 0000000..35a04ce --- /dev/null +++ b/test/fixtures/http-es6-modules.json @@ -0,0 +1,50 @@ +[ + { + "url": "http://localhost:9000/js/index.js", + "ranges": [ + { + "start": 0, + "end": 277 + } + ], + "text": "import docReady from \"./utils/doc_ready.js\";\nimport Record from \"./models/record.js\";\nimport RecordView from \"./views/record.js\";\n\ndocReady(() => {\n let record = new Record(3, 4);\n let recordView = new RecordView(record);\n document.body.appendChild(recordView.render())\n});\n" + }, + { + "url": "http://localhost/js/utils/doc_ready.js", + "ranges": [ + { + "start": 0, + "end": 80 + }, + { + "start": 146, + "end": 150 + } + ], + "text": "export default (fn) => {\n if (document.readyState != \"loading\") {\n fn();\n } else {\n document.addEventListener(\"DOMContentLoaded\", fn);\n }\n};\n" + }, + { + "url": "http://localhost:9000/js/models/record.js", + "ranges": [ + { + "start": 0, + "end": 90 + } + ], + "text": "export default class Record {\n constructor(x, y) {\n this.x = x;\n this.y = y;\n }\n}\n" + }, + { + "url": "http://localhost:9000/js/views/record.js", + "ranges": [ + { + "start": 0, + "end": 298 + }, + { + "start": 301, + "end": 304 + } + ], + "text": "export default class RecordView {\n constructor(record) {\n this.record = record\n }\n\n render() {\n this.elem = document.createElement(\"div\");\n this.elem.appendChild(\n document.createTextNode(\n \"x: \" + this.record.x + \"; y: \" + this.record.y\n )\n );\n return this.elem;\n }\n}" + } +] \ No newline at end of file diff --git a/test/output-files.js b/test/output-files.js index 112c6ac..a72d88d 100644 --- a/test/output-files.js +++ b/test/output-files.js @@ -3,6 +3,7 @@ const OutputFiles = require('../lib/output-files') const rimraf = require('rimraf') const pathLib = require('path') +const url = require('url') const storagePath = '.nyc_output/js' const storagePathTop = '.nyc_output' @@ -19,7 +20,7 @@ describe('output-files', () => { // 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.include(storagePath + '/block-else-not-covered-1.js') + newPath.should.include(storagePath + '/sample_js/block-else-not-covered-1.js') }) it('handle multiple files with same name, and replace in json', () => { @@ -59,15 +60,24 @@ describe('output-files', () => { coverageInfo[1].url.should.include('puppeteerTemp-inline-1.js') }) + it('appropriately handles modules required via http/https', () => { + const fixture = require('./fixtures/http-es6-modules.json') + const coverageInfo = OutputFiles(fixture).getTransformedCoverage() + + coverageInfo[0].url.should.include('js/index.js') + coverageInfo[1].url.should.include('js/utils/doc_ready.js') + coverageInfo[2].url.should.include('js/models/record.js') + coverageInfo[3].url.should.include('js/views/record.js') + }) + function cleanupCoverage () { rimraf.sync(storagePathTop) } // 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 pathLib.resolve(storagePath, splitUrl[splitUrl.length - 1]) + function movedUrl (path) { + var urlPath = new url.URL(path) + urlPath = urlPath.pathname.substring(1) + return pathLib.resolve(storagePath, urlPath) } })