UNPKG

karma-htmlfile-reporter

Version:
296 lines (243 loc) 11.6 kB
var os = require('os'); var path = require('path'); var fs = require('fs'); var builder = require('xmlbuilder'); var HTMLReporter = function(baseReporterDecorator, config, emitter, logger, helper, formatError) { var outputFile = config.htmlReporter.outputFile; var pageTitle = config.htmlReporter.pageTitle || 'Unit Test Results'; var subPageTitle = config.htmlReporter.subPageTitle || false; var groupSuites = config.htmlReporter.groupSuites || false; var useLegacyStyle = config.htmlReporter.useLegacyStyle || false; var useCompactStyle = config.htmlReporter.useCompactStyle || false; var showOnlyFailed = config.htmlReporter.showOnlyFailed || false; var log = logger.create('reporter.html'); var html; var body; var suites; var htmlCreated = false; var lastSuiteName; var pendingFileWritings = 0; var fileWritingFinished = function() {}; var allMessages = []; var allErrors = []; var self = this; baseReporterDecorator(this); // TODO: remove if public version of this method is available var basePathResolve = function(relativePath) { if (helper.isUrlAbsolute(relativePath)) { return relativePath; } if (!helper.isDefined(config.basePath) || !helper.isDefined(relativePath)) { return ''; } return path.resolve(config.basePath, relativePath); }; var htmlHelpers = { createHead: function() { var head = html.ele('head'); head.ele('meta', {charset: 'utf-8'}); head.ele('title', {}, pageTitle + (subPageTitle ? ' - ' + subPageTitle : '')); head.ele('style', {type: 'text/css'}, 'html,body{font-family:Arial,sans-serif;font-size:1rem;margin:0;padding:0;}body{padding:10px 40px;}h1{margin-bottom:0;}h2{margin-top:0;margin-bottom:0;color:#999;}table{width:100%;margin-top:20px;margin-bottom:20px;table-layout:fixed;}tr.header{background:#ddd;font-weight:bold;border-bottom:none;}td{padding:7px;border-top:none;border-left:1px black solid;border-bottom:1px black solid;border-right:none;word-break:break-all;word-wrap:break-word;}tr.pass td{color:#003b07;background:#86e191;}tr.skip td{color:#7d3a00;background:#ffd24a;}tr.fail td{color:#5e0e00;background:#ff9c8a;}tr:first-child td{border-top:1px black solid;}td:last-child{border-right:1px black solid;}tr.overview{font-weight:bold;color:#777;}tr.overview td{padding-bottom:0px;border-bottom:none;}tr.system-out td{color:#777;}tr.system-errors td{color:#f00;}hr{height:2px;margin:30px 0;background:#000;border:none;}section{margin-top:40px;}h3{margin:6px 0;}.overview{color:#333;font-weight:bold;}.system-out{margin:0.4rem 0;}.system-errors{color:#a94442}.spec{padding:0.8rem;margin:0.3rem 0;}.spec--pass{color:#3c763d;background-color:#dff0d8;border:1px solid #d6e9c6;}.spec--skip{color:#8a6d3b;background-color:#fcf8e3;border:1px solid #faebcc;}.spec--fail{color:#a94442;background-color:#f2dede;border:1px solid #ebccd1;}.spec--group{color:#636363;background-color:#f0f0f0;border:1px solid #e6e6e6;margin:0;}.spec--group:not(:first-of-type){margin:20px 0 0 0;}.spec__title{display:inline;}.spec__suite{display:inline;}.spec__descrip{font-weight:normal;}.spec__status{float:right;}.spec__log{padding-left: 2.3rem;}.hidden{display:none;}body.compact .spec p{margin-top:0;margin-bottom:0.5rem;}body.compact .spec,body.compact tr,body.compact .overview,body.compact .system-out,body.compact .system-errors{font-size:0.85rem;}body.compact .spec{padding:0.3rem 0.5rem;}body.compact section{margin-top:30px;}'); }, createBody: function() { body = html.ele('body', {class:useCompactStyle ? 'compact' : ''}); body.ele('h1', {}, pageTitle); if (subPageTitle) { body.ele('h2', {}, subPageTitle); } } }; var createHtmlResults = function(browser) { var suite; var header; var overview; var timestamp = (new Date()).toLocaleString(); if (!suites) { self.onRunStart(browser); } if (useLegacyStyle) { suite = suites[browser.id] = body.ele('table', {cellspacing:'0', cellpadding:'0', border:'0'}); suite.ele('tr', {class:'overview'}).ele('td', {colspan:'3', title:browser.fullName}, 'Browser: ' + browser.name); suite.ele('tr', {class:'overview'}).ele('td', {colspan:'3'}, 'Timestamp: ' + timestamp); suites[browser.id]['results'] = suite.ele('tr').ele('td', {colspan:'3'}); header = suite.ele('tr', {class:'header'}); header.ele('td', {}, 'Status'); header.ele('td', {}, 'Spec'); header.ele('td', {}, 'Suite / Results'); body.ele('hr'); } else { suite = suites[browser.id] = body.ele('section', {}); overview = suite.ele('header', {class:'overview'}); // Assemble the Overview overview.ele('div', {class:'browser'}, 'Browser: ' + browser.name); overview.ele('div', {class:'timestamp'}, 'Timestamp: ' + timestamp); // Create paragraph tag for test results to be placed in later suites[browser.id]['results'] = overview.ele('p', {class:'results'}); } }; var initializeHtmlForBrowser = function(browser) { if (!htmlCreated) { html = builder.create('html', null, 'html', { headless: true }); html.doctype(); htmlHelpers.createHead(); htmlHelpers.createBody(); htmlCreated = true; } }; this.adapters = [function(msg) { allMessages.push(msg); }]; this.onRunStart = function(browsers) { suites = {}; browsers.forEach(initializeHtmlForBrowser); }; this.onBrowserStart = function(browser) { initializeHtmlForBrowser(browser); createHtmlResults(browser); }; this.onBrowserError = function(browser, error) { initializeHtmlForBrowser(browser); createHtmlResults(browser); allErrors.push(formatError(error)); }; this.onBrowserComplete = function(browser) { var suite = suites[browser.id]; var result = browser.lastResult; if (suite && suite['results']) { suite['results'].txt(result.total + ' tests / '); suite['results'].txt((result.disconnected || result.error ? 1 : 0) + ' errors / '); suite['results'].txt(result.failed + ' failures / '); suite['results'].txt(result.skipped + ' skipped / '); suite['results'].txt('runtime: ' + ((result.netTime || 0) / 1000) + 's'); if (allMessages.length > 0) { if (useLegacyStyle) { suite.ele('tr', {class:'system-out'}).ele('td', {colspan:'3'}).raw('<strong>System output:</strong><br />' + allMessages.join('<br />')); } else { suite.ele('div', {class:'system-out'}).raw('<strong>System output:</strong><br />' + allMessages.join('<br />')); } allMessages = []; } if (allErrors.length > 0) { if (useLegacyStyle) { suite.ele('tr', {class:'system-errors'}).ele('td', {colspan:'3'}).raw('<strong>Errors:</strong><br />' + allErrors.join('<br />')); } else { suite.ele('div', {class:'system-errors'}).raw('<strong>Errors:</strong><br />' + allErrors.join('<br />')); } allErrors = []; } } suites[browser.id] = null; }; this.onRunComplete = function(browsers) { var htmlToOutput = html; if (htmlToOutput) { pendingFileWritings++; config.basePath = path.resolve(config.basePath || '.'); outputFile = basePathResolve(outputFile); helper.normalizeWinPath(outputFile); helper.mkdirIfNotExists(path.dirname(outputFile), function() { if(!htmlToOutput) { log.warn('Cannot write HTML report\n\t No output!'); if (!--pendingFileWritings) { fileWritingFinished(); } return; } fs.writeFile(outputFile, htmlToOutput.end({pretty: true}), function(err) { if (err) { log.warn('Cannot write HTML report\n\t' + err.message); } else { log.debug('HTML results written to "%s".', outputFile); } if (!--pendingFileWritings) { fileWritingFinished(); } }); }); } else { log.error('HTML report was not created\n\t'); } html = null; allMessages.length = 0; allErrors.length = 0; htmlCreated = false; }; this.specSuccess = this.specSkipped = this.specFailure = function(browser, result) { var currentSuite = result.suite; var suiteName = currentSuite.concat(); var currentSuiteName = currentSuite[0]; var isNewSuite = false; var specClass = result.skipped ? 'skip' : (result.success ? 'pass' : 'fail'); var specStatus = result.skipped ? 'Skipped' : (result.success ? ('Passed in ' + ((result.time || 0) / 1000) + 's') : 'Failed'); var spec; var specGroup; var specHeader; var specTitle; var suiteColumn; if (lastSuiteName !== currentSuiteName) { isNewSuite = true; lastSuiteName = currentSuiteName; } if (currentSuite.length > 1) { suiteName.shift(); } if (!showOnlyFailed || (showOnlyFailed && !result.success)) { if (useLegacyStyle) { spec = suites[browser.id].ele('tr', {class:specClass}); spec.ele('td', {}, specStatus); spec.ele('td', {}, result.description); suiteColumn = spec.ele('td', {}).raw(currentSuite.join(' &raquo; ')); } else { if (groupSuites && isNewSuite) { specGroup = suites[browser.id].ele('div', {class: 'spec spec--group'}); specGroup.ele('h3', {class:'spec__header'}).raw(currentSuiteName); } spec = suites[browser.id].ele('div', {class: 'spec spec--' + specClass, style: (groupSuites ? ('margin-left:' + ((currentSuite.length - 1) * 20) + 'px;') : '')}); // Create spec header specHeader = spec.ele('h3', {class:'spec__header'}); // Assemble the spec title specTitle = specHeader.ele('div', {class:'spec__title'}); specTitle.ele('p', {class:'spec__suite' + (groupSuites ? ((suiteName[0] !== currentSuiteName || suiteName.length > 1) ? '' : ' hidden') : '')}).raw(suiteName.join(' &raquo; ')); specTitle.ele('em', {class:'spec__descrip'}, result.description); // Display spec result specHeader.ele('div', {class:'spec__status'}, specStatus); } } if (!result.success) { if (useLegacyStyle) { result.log.forEach(function(err) { suiteColumn.raw('<br />' + formatError(err).replace(/</g,'&lt;').replace(/>/g,'&gt;')); }); } else { // Error Messages suiteColumn = spec.ele('p', {class:'spec__log'}); result.log.forEach(function(err, index) { var message = (index === 0) ? '' : '<br />'; message += formatError(err).replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/(?:\r\n|\r|\n)/g, '<br />'); suiteColumn.raw(message); }); } } }; // wait for writing all the html files, before exiting this.onExit = function (done) { if (pendingFileWritings) { fileWritingFinished = done; } else { done(); } }; }; HTMLReporter.prototype._repeat = function(n, str) { var res = []; var i; for (i = 0; i < n; ++i) { res.push(str); } return res.join(''); }; HTMLReporter.$inject = ['baseReporterDecorator', 'config', 'emitter', 'logger', 'helper', 'formatError']; // PUBLISH DI MODULE module.exports = { 'reporter:html': ['type', HTMLReporter] };