UNPKG

grover

Version:

PhantomJS wrapper for YUITest

458 lines (392 loc) 12.6 kB
var istanbul = require('istanbul'); var Table = require('cli-table'); var util = require('../lib/index'); var fs = require('fs'); var path = require('path'); var coverageType = 'yuitest'; var coverageInfo = {}; var options; var log = require('./log'); var rimraf = require('rimraf'); exports.__defineGetter__('coverageInfo', function() { return coverageInfo; }); exports.__defineSetter__('coverageInfo', function(o) { coverageInfo = o; return coverageInfo; }); exports.options = function(o) { options = o; }; exports.init = function() { coverageType = 'yuitest'; coverageInfo = {}; }; var isIstanbul = function(json) { json = json || {}; var first = Object.keys(json)[0], ret = false; if (first && json[first].s !== undefined && json[first].fnMap !== undefined) { ret = true; } if (json.s !== undefined && json.fnMap !== undefined) { ret = true; } if (ret) { coverageType = 'istanbul'; } return ret; }; exports.isIstanbul = isIstanbul; var set = function(json) { var d = {}, i; for (i in json) { if (json.hasOwnProperty(i)) { d[i] = json[i]; delete d[i].code; } } if (isIstanbul(json)) { if (!Array.isArray(coverageInfo)) { coverageInfo = []; } coverageInfo.push(json); } else { for (i in json) { if (json.hasOwnProperty(i)) { coverageInfo[i] = coverageInfo[i] || {}; coverageInfo[i].path = i; coverageInfo[i].lines = json[i].lines; coverageInfo[i].functions = json[i].functions; /*jshint loopfunc: true */ ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) { coverageInfo[i][prop] = coverageInfo[i][prop] || 0; coverageInfo[i][prop] = Math.max(coverageInfo[i][prop], json[i][prop], 0); }); } } } }; exports.set = set; var pathSort = function(a, b) { if (!a.path || !b.path) { return 0; } var an = a.path.toLowerCase(), bn = b.path.toLowerCase(), ret = 0; if (an < bn) { ret = -1; } if (an > bn) { ret = 1; } return ret; }; exports.pathSort = pathSort; var printYUITestReport = function() { var items = [], table = new Table({ head: ['path', 'lines', '%', 'functions', '%' ], colAligns: [ 'left', 'center', 'right', 'center', 'right' ], style: { 'padding-left': 2, 'padding-right': 2, head: ['blue'] } }), totals = { coveredLines: 0, coveredFunctions: 0, calledLines: 0, calledFunctions: 0 }; Object.keys(coverageInfo).forEach(function(item) { items.push(coverageInfo[item]); }); items.sort(pathSort); items.forEach(function(row) { var err, percentLine = Math.floor((row.calledLines / row.coveredLines) * 100), percentFunction = Math.floor((row.calledFunctions / row.coveredFunctions) * 100), cell = [ row.path, row.calledLines + '/' + row.coveredLines, percentLine + '%', row.calledFunctions + '/' + row.coveredFunctions, percentFunction + '%' ]; ['calledLines', 'calledFunctions', 'coveredLines', 'coveredFunctions'].forEach(function(prop) { totals[prop] += row[prop]; }); if (percentLine <= options.coverageWarn) { err = true; cell[1] = util.color(String(cell[1]), 'red'); cell[2] = util.color(String(cell[2]), 'red'); } if (percentFunction <= options.coverageWarn) { err = true; cell[3] = util.color(String(cell[3]), 'red'); cell[4] = util.color(String(cell[4]), 'red'); } if (err) { cell[0] = util.color(util.bad, 'red') + ' ' + cell[0]; } else { cell[0] = util.color(util.good, 'green') + ' ' + cell[0]; } table.push(cell); }); table.push([ 'total', totals.calledLines + '/' + totals.coveredLines, Math.floor((totals.calledLines / totals.coveredLines) * 100) + '%', totals.calledFunctions + '/' + totals.coveredFunctions, Math.floor((totals.calledFunctions / totals.coveredFunctions) * 100) + '%' ]); log.log(table.toString()); }; var printIstanbulReport = function() { var collect = new istanbul.Collector(), report = istanbul.Report.create('text'), summary = istanbul.Report.create('text-summary'); coverageInfo.forEach(function(coverage) { collect.add(coverage); }); log.log(''); report.writeReport(collect); summary.writeReport(collect); }; var report = function(options) { if (options.coverdir) { return; } if (coverageType === 'istanbul') { printIstanbulReport(); } else { printYUITestReport(); } }; exports.report = report; var getYUIStatus = function(coverage) { var cov = { lines: 0, hit: 0, miss: 0, percent: 0 }, i, str; for (i in coverage) { if (coverage.hasOwnProperty(i)) { cov.lines = cov.lines + coverage[i].coveredLines; cov.miss = cov.miss + (coverage[i].coveredLines - coverage[i].calledLines); cov.hit = cov.hit + coverage[i].calledLines; } } cov.percent = Math.floor((cov.hit / cov.lines) * 100); str = util.color(' ' + cov.percent + '%', 'blue'); return str; }; var getIstanbulStatus = function(coverage) { var collect = new istanbul.Collector(), lines = [], str = '', pct = 0; collect.add(coverage); collect.files().forEach(function(file) { var fileCoverage = collect.fileCoverageFor(file), summary = istanbul.utils.summarizeFileCoverage(fileCoverage); lines.push(summary.lines.pct); pct += summary.lines.pct; }); str = util.color(' ' + Math.floor(pct / lines.length) + '%', 'blue'); return str; }; exports.status = function(json) { var out; if (isIstanbul(json)) { coverageType = 'istanbul'; out = getIstanbulStatus(json); } else { out = getYUIStatus(json); } return out; }; var generateIstanbulHTML = function(options) { var report = istanbul.Report.create('html', { dir: options.istanbulReport }), lcovFile = options.coverageFileName, collector = new istanbul.Collector(); coverageInfo.forEach(function (item) { collector.add(item); }); report.writeReport(collector, true); if (lcovFile) { report = istanbul.Report.create('lcovonly', { dir: options.istanbulReport }); report.writeReport(collector, true); } util.exit(0); }; var getIstanbulCoverageReport = function (options, skip) { if (options.istanbulReport && !skip) { return generateIstanbulHTML(options); } var lcovFile = options.coverageFileName, prefix = options.sourceFilePrefix || '', report = istanbul.Report.create('lcovonly', { dir: path.dirname(lcovFile) }), str = '', parts = [], iFile, collector = new istanbul.Collector(); coverageInfo.forEach(function (item) { collector.add(item); }); report.writeReport(collector, true); iFile = path.join(path.dirname(lcovFile), 'lcov.info'); str = fs.readFileSync(iFile, 'utf8'); parts = str.split('\n'); str = ''; parts.forEach(function(line, num) { if (line.indexOf('SF:') === 0) { var file = line.replace('SF:', ''); file = path.resolve(prefix, file); parts[num] = 'SF:' + file; } }); //Remove the istanbul file fs.unlinkSync(iFile); return parts.join('\n'); }; exports.getIstanbulCoverageReport = getIstanbulCoverageReport; var getYUITestLcov = function (testName, prefix, data) { var lcovreport = {}, file = data.path, match, name, line, exec, fn, fnkeys, lineKeys, j = 0, fileName = (prefix ? path.resolve(prefix, file) : path.resolve(process.cwd(), file)); if (!util.existsSync(fileName)) { log.log('Failed to find source file:', fileName); util.exit(1); } lcovreport.TN = 'lcov.info'; lcovreport.SF = fileName; lcovreport.DA = []; lcovreport.FN = []; lcovreport.FNDA = []; lcovreport.FNF = data.coveredFunctions; lcovreport.FNH = data.calledFunctions; lcovreport.LF = data.coveredLines; lcovreport.LH = data.calledLines; fnkeys = Object.keys(data.functions); for (j; j < fnkeys.length; j += 1) { fn = fnkeys[j]; exec = data.functions[fn]; match = fn.match(/^([\w ()_]+):(\d)+$/); name = match[1]; line = match[2]; lcovreport.FN.push({name: name, line: line}); lcovreport.FNDA.push({name: name, exec: exec}); } lineKeys = Object.keys(data.lines); for (j = 0; j < lineKeys.length; j += 1) { line = lineKeys[j]; exec = data.lines[line]; lcovreport.DA.push({line: line, exec: exec}); } return lcovreport; }; exports.getYUITestLcov = getYUITestLcov; var getYUITestLcovReport = function (report) { var text = '', i = 0, fn, line; text += "TN:" + report.TN + "\n"; text += "SF:" + report.SF + "\n"; for (i; i < report.FN.length; i += 1) { fn = report.FN[i]; text += "FN:" + fn.line + "," + fn.name + "\n"; } text += "\n"; for (i = 0; i < report.FNDA.length; i += 1) { fn = report.FNDA[i]; text += "FNDA:" + fn.exec + "," + fn.name + "\n"; } text += "\n"; text += "FNF:" + report.FNF + "\n"; text += "FNH:" + report.FNH + "\n"; for (i = 0; i < report.DA.length; i += 1) { line = report.DA[i]; text += "DA:" + line.line + "," + line.exec + "\n"; } text += "\n"; text += "LF:" + report.LF + "\n"; text += "LH:" + report.LH + "\n\n"; text += "end_of_record\n\n"; return text; }; exports.getYUITestLcovReport = getYUITestLcovReport; var getCoverageReport = function(options, skip) { var prefix = options.sourceFilePrefix || '', lcovStr = ''; if (coverageType === 'istanbul') { lcovStr = getIstanbulCoverageReport(options, skip); } else { Object.keys(coverageInfo).forEach(function(file) { lcovStr += getYUITestLcovReport(getYUITestLcov(file, prefix, coverageInfo[file])); }); } return lcovStr; }; exports.getCoverageReport = getCoverageReport; var writeIstanbulReport = function(options) { rimraf.sync(options.coverdir); var collect = new istanbul.Collector(), report = istanbul.Report.create('lcov', { dir: options.coverdir }), jsonReport = istanbul.Report.create('json', { dir: options.coverdir }); coverageInfo.forEach(function(coverage) { collect.add(coverage); }); log.log('generating istanbul LCOV report, saving here: ' + options.coverdir); report.writeReport(collect, true); jsonReport.writeReport(collect, true); }; var save = function(options) { var lcovFile = options.coverageFileName, lcovStr = getCoverageReport(options), gen = ''; if (coverageType === 'istanbul' && options.coverdir) { return writeIstanbulReport(options); } if (coverageType === 'istanbul') { gen = 'genhtml -o ./out/ -s ' + path.basename(lcovFile); } else { gen = 'genhtml -o ./out/ -s --no-branch-coverage ' + path.basename(lcovFile); } if (lcovFile) { lcovStr = getCoverageReport(options, true); if (util.existsSync(lcovFile)) { //Remove the file before writing it fs.unlinkSync(lcovFile); } fs.writeFileSync(lcovFile, lcovStr, 'utf8'); util.log('Saved LCOV data to: ' + lcovFile); util.log('To generate the report:'); log.log(' ', 'cd ' + path.dirname(lcovFile) + ';'); log.log(' ', gen); } }; exports.save = save;