jasmineRequire.HtmlReporter = function(j$) { function ResultsStateBuilder() { this.topResults = new j$.ResultsNode({}, '', null); this.currentParent = this.topResults; this.specsExecuted = 0; this.failureCount = 0; this.pendingSpecCount = 0; } ResultsStateBuilder.prototype.suiteStarted = function(result) { this.currentParent.addChild(result, 'suite'); this.currentParent = this.currentParent.last(); }; ResultsStateBuilder.prototype.suiteDone = function(result) { this.currentParent.updateResult(result); if (this.currentParent !== this.topResults) { this.currentParent = this.currentParent.parent; } if (result.status === 'failed') { this.failureCount++; } }; ResultsStateBuilder.prototype.specStarted = function(result) {}; ResultsStateBuilder.prototype.specDone = function(result) { this.currentParent.addChild(result, 'spec'); if (result.status !== 'excluded') { this.specsExecuted++; } if (result.status === 'failed') { this.failureCount++; } if (result.status == 'pending') { this.pendingSpecCount++; } }; ResultsStateBuilder.prototype.jasmineDone = function(result) { if (result.failedExpectations) { this.failureCount += result.failedExpectations.length; } }; function HtmlReporter(options) { function config() { return (options.env && options.env.configuration()) || {}; } const getContainer = options.getContainer; const createElement = options.createElement; const createTextNode = options.createTextNode; const navigateWithNewParam = options.navigateWithNewParam || function() {}; const addToExistingQueryString = options.addToExistingQueryString || defaultQueryString; const filterSpecs = options.filterSpecs; let htmlReporterMain; let symbols; const deprecationWarnings = []; const failures = []; this.initialize = function() { clearPrior(); htmlReporterMain = createDom( 'div', { className: 'jasmine_html-reporter' }, createDom( 'div', { className: 'jasmine-banner' }, createDom('a', { className: 'jasmine-title', href: 'http://jasmine.github.io/', target: '_blank' }), createDom('span', { className: 'jasmine-version' }, j$.version) ), createDom('ul', { className: 'jasmine-symbol-summary' }), createDom('div', { className: 'jasmine-alert' }), createDom( 'div', { className: 'jasmine-results' }, createDom('div', { className: 'jasmine-failures' }) ) ); getContainer().appendChild(htmlReporterMain); }; let totalSpecsDefined; this.jasmineStarted = function(options) { totalSpecsDefined = options.totalSpecsDefined || 0; }; const summary = createDom('div', { className: 'jasmine-summary' }); const stateBuilder = new ResultsStateBuilder(); this.suiteStarted = function(result) { stateBuilder.suiteStarted(result); }; this.suiteDone = function(result) { stateBuilder.suiteDone(result); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'suite'); }; this.specStarted = function(result) { stateBuilder.specStarted(result); }; this.specDone = function(result) { stateBuilder.specDone(result); if (noExpectations(result)) { const noSpecMsg = "Spec '" + result.fullName + "' has no expectations."; if (result.status === 'failed') { // eslint-disable-next-line no-console console.error(noSpecMsg); } else { // eslint-disable-next-line no-console console.warn(noSpecMsg); } } if (!symbols) { symbols = find('.jasmine-symbol-summary'); } symbols.appendChild( createDom('li', { className: this.displaySpecInCorrectFormat(result), id: 'spec_' + result.id, title: result.fullName }) ); if (result.status === 'failed') { failures.push(failureDom(result)); } addDeprecationWarnings(result, 'spec'); }; this.displaySpecInCorrectFormat = function(result) { return noExpectations(result) && result.status === 'passed' ? 'jasmine-empty' : this.resultStatus(result.status); }; this.resultStatus = function(status) { if (status === 'excluded') { return config().hideDisabled ? 'jasmine-excluded-no-display' : 'jasmine-excluded'; } return 'jasmine-' + status; }; this.jasmineDone = function(doneResult) { stateBuilder.jasmineDone(doneResult); const banner = find('.jasmine-banner'); const alert = find('.jasmine-alert'); const order = doneResult && doneResult.order; alert.appendChild( createDom( 'span', { className: 'jasmine-duration' }, 'finished in ' + doneResult.totalTime / 1000 + 's' ) ); banner.appendChild(optionsMenu(config())); if (stateBuilder.specsExecuted < totalSpecsDefined) { const skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 const skippedLink = (window.location.pathname || '') + addToExistingQueryString('spec', ''); alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-skipped' }, createDom( 'a', { href: skippedLink, title: 'Run all specs' }, skippedMessage ) ) ); } let statusBarMessage = ''; let statusBarClassName = 'jasmine-overall-result jasmine-bar '; const globalFailures = (doneResult && doneResult.failedExpectations) || []; const failed = stateBuilder.failureCount + globalFailures.length > 0; if (totalSpecsDefined > 0 || failed) { statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); if (stateBuilder.pendingSpecCount) { statusBarMessage += ', ' + pluralize('pending spec', stateBuilder.pendingSpecCount); } } if (doneResult.overallStatus === 'passed') { statusBarClassName += ' jasmine-passed '; } else if (doneResult.overallStatus === 'incomplete') { statusBarClassName += ' jasmine-incomplete '; statusBarMessage = 'Incomplete: ' + doneResult.incompleteReason + ', ' + statusBarMessage; } else { statusBarClassName += ' jasmine-failed '; } let seedBar; if (order && order.random) { seedBar = createDom( 'span', { className: 'jasmine-seed-bar' }, ', randomized with seed ', createDom( 'a', { title: 'randomized with seed ' + order.seed, href: seedHref(order.seed) }, order.seed ) ); } alert.appendChild( createDom( 'span', { className: statusBarClassName }, statusBarMessage, seedBar ) ); const errorBarClassName = 'jasmine-bar jasmine-errored'; const afterAllMessagePrefix = 'AfterAll '; for (let i = 0; i < globalFailures.length; i++) { alert.appendChild( createDom( 'span', { className: errorBarClassName }, globalFailureMessage(globalFailures[i]) ) ); } function globalFailureMessage(failure) { if (failure.globalErrorType === 'load') { const prefix = 'Error during loading: ' + failure.message; if (failure.filename) { return ( prefix + ' in ' + failure.filename + ' line ' + failure.lineno ); } else { return prefix; } } else if (failure.globalErrorType === 'afterAll') { return afterAllMessagePrefix + failure.message; } else { return failure.message; } } addDeprecationWarnings(doneResult); for (let i = 0; i < deprecationWarnings.length; i++) { const children = []; let context; switch (deprecationWarnings[i].runnableType) { case 'spec': context = '(in spec: ' + deprecationWarnings[i].runnableName + ')'; break; case 'suite': context = '(in suite: ' + deprecationWarnings[i].runnableName + ')'; break; default: context = ''; } deprecationWarnings[i].message.split('\n').forEach(function(line) { children.push(line); children.push(createDom('br')); }); children[0] = 'DEPRECATION: ' + children[0]; children.push(context); if (deprecationWarnings[i].stack) { children.push(createExpander(deprecationWarnings[i].stack)); } alert.appendChild( createDom( 'span', { className: 'jasmine-bar jasmine-warning' }, children ) ); } const results = find('.jasmine-results'); results.appendChild(summary); summaryList(stateBuilder.topResults, summary); if (failures.length) { alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-spec-list' }, createDom('span', {}, 'Spec List | '), createDom( 'a', { className: 'jasmine-failures-menu', href: '#' }, 'Failures' ) ) ); alert.appendChild( createDom( 'span', { className: 'jasmine-menu jasmine-bar jasmine-failure-list' }, createDom( 'a', { className: 'jasmine-spec-list-menu', href: '#' }, 'Spec List' ), createDom('span', {}, ' | Failures ') ) ); find('.jasmine-failures-menu').onclick = function() { setMenuModeTo('jasmine-failure-list'); return false; }; find('.jasmine-spec-list-menu').onclick = function() { setMenuModeTo('jasmine-spec-list'); return false; }; setMenuModeTo('jasmine-failure-list'); const failureNode = find('.jasmine-failures'); for (let i = 0; i < failures.length; i++) { failureNode.appendChild(failures[i]); } } }; return this; function failureDom(result) { const failure = createDom( 'div', { className: 'jasmine-spec-detail jasmine-failed' }, failureDescription(result, stateBuilder.currentParent), createDom('div', { className: 'jasmine-messages' }) ); const messages = failure.childNodes[1]; for (let i = 0; i < result.failedExpectations.length; i++) { const expectation = result.failedExpectations[i]; messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, expectation.message ) ); messages.appendChild( createDom( 'div', { className: 'jasmine-stack-trace' }, expectation.stack ) ); } if (result.failedExpectations.length === 0) { messages.appendChild( createDom( 'div', { className: 'jasmine-result-message' }, 'Spec has no expectations' ) ); } if (result.debugLogs) { messages.appendChild(debugLogTable(result.debugLogs)); } return failure; } function debugLogTable(debugLogs) { const tbody = createDom('tbody'); debugLogs.forEach(function(entry) { tbody.appendChild( createDom( 'tr', {}, createDom('td', {}, entry.timestamp.toString()), createDom( 'td', { className: 'jasmine-debug-log-msg' }, entry.message ) ) ); }); return createDom( 'div', { className: 'jasmine-debug-log' }, createDom( 'div', { className: 'jasmine-debug-log-header' }, 'Debug logs' ), createDom( 'table', {}, createDom( 'thead', {}, createDom( 'tr', {}, createDom('th', {}, 'Time (ms)'), createDom('th', {}, 'Message') ) ), tbody ) ); } function summaryList(resultsTree, domParent) { let specListNode; for (let i = 0; i < resultsTree.children.length; i++) { const resultNode = resultsTree.children[i]; if (filterSpecs && !hasActiveSpec(resultNode)) { continue; } if (resultNode.type === 'suite') { const suiteListNode = createDom( 'ul', { className: 'jasmine-suite', id: 'suite-' + resultNode.result.id }, createDom( 'li', { className: 'jasmine-suite-detail jasmine-' + resultNode.result.status }, createDom( 'a', { href: specHref(resultNode.result) }, resultNode.result.description ) ) ); summaryList(resultNode, suiteListNode); domParent.appendChild(suiteListNode); } if (resultNode.type === 'spec') { if (domParent.getAttribute('class') !== 'jasmine-specs') { specListNode = createDom('ul', { className: 'jasmine-specs' }); domParent.appendChild(specListNode); } let specDescription = resultNode.result.description; if (noExpectations(resultNode.result)) { specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; } if (resultNode.result.status === 'pending') { if (resultNode.result.pendingReason !== '') { specDescription += ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; } else { specDescription += ' PENDING'; } } specListNode.appendChild( createDom( 'li', { className: 'jasmine-' + resultNode.result.status, id: 'spec-' + resultNode.result.id }, createDom( 'a', { href: specHref(resultNode.result) }, specDescription ), createDom( 'span', { className: 'jasmine-spec-duration' }, '(' + resultNode.result.duration + 'ms)' ) ) ); } } } function optionsMenu(config) { const optionsMenuDom = createDom( 'div', { className: 'jasmine-run-options' }, createDom('span', { className: 'jasmine-trigger' }, 'Options'), createDom( 'div', { className: 'jasmine-payload' }, createDom( 'div', { className: 'jasmine-stop-on-failure' }, createDom('input', { className: 'jasmine-fail-fast', id: 'jasmine-fail-fast', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-fail-fast' }, 'stop execution on spec failure' ) ), createDom( 'div', { className: 'jasmine-throw-failures' }, createDom('input', { className: 'jasmine-throw', id: 'jasmine-throw-failures', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-throw-failures' }, 'stop spec on expectation failure' ) ), createDom( 'div', { className: 'jasmine-random-order' }, createDom('input', { className: 'jasmine-random', id: 'jasmine-random-order', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-random-order' }, 'run tests in random order' ) ), createDom( 'div', { className: 'jasmine-hide-disabled' }, createDom('input', { className: 'jasmine-disabled', id: 'jasmine-hide-disabled', type: 'checkbox' }), createDom( 'label', { className: 'jasmine-label', for: 'jasmine-hide-disabled' }, 'hide disabled tests' ) ) ) ); const failFastCheckbox = optionsMenuDom.querySelector( '#jasmine-fail-fast' ); failFastCheckbox.checked = config.stopOnSpecFailure; failFastCheckbox.onclick = function() { navigateWithNewParam('stopOnSpecFailure', !config.stopOnSpecFailure); }; const throwCheckbox = optionsMenuDom.querySelector( '#jasmine-throw-failures' ); throwCheckbox.checked = config.stopSpecOnExpectationFailure; throwCheckbox.onclick = function() { navigateWithNewParam( 'stopSpecOnExpectationFailure', !config.stopSpecOnExpectationFailure ); }; const randomCheckbox = optionsMenuDom.querySelector( '#jasmine-random-order' ); randomCheckbox.checked = config.random; randomCheckbox.onclick = function() { navigateWithNewParam('random', !config.random); }; const hideDisabled = optionsMenuDom.querySelector( '#jasmine-hide-disabled' ); hideDisabled.checked = config.hideDisabled; hideDisabled.onclick = function() { navigateWithNewParam('hideDisabled', !config.hideDisabled); }; const optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'), optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'), isOpen = /\bjasmine-open\b/; optionsTrigger.onclick = function() { if (isOpen.test(optionsPayload.className)) { optionsPayload.className = optionsPayload.className.replace( isOpen, '' ); } else { optionsPayload.className += ' jasmine-open'; } }; return optionsMenuDom; } function failureDescription(result, suite) { const wrapper = createDom( 'div', { className: 'jasmine-description' }, createDom( 'a', { title: result.description, href: specHref(result) }, result.description ) ); let suiteLink; while (suite && suite.parent) { wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild); suiteLink = createDom( 'a', { href: suiteHref(suite) }, suite.result.description ); wrapper.insertBefore(suiteLink, wrapper.firstChild); suite = suite.parent; } return wrapper; } function suiteHref(suite) { const els = []; while (suite && suite.parent) { els.unshift(suite.result.description); suite = suite.parent; } // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', els.join(' ')) ); } function addDeprecationWarnings(result, runnableType) { if (result && result.deprecationWarnings) { for (let i = 0; i < result.deprecationWarnings.length; i++) { const warning = result.deprecationWarnings[i].message; deprecationWarnings.push({ message: warning, stack: result.deprecationWarnings[i].stack, runnableName: result.fullName, runnableType: runnableType }); } } } function createExpander(stackTrace) { const expandLink = createDom('a', { href: '#' }, 'Show stack trace'); const root = createDom( 'div', { className: 'jasmine-expander' }, expandLink, createDom( 'div', { className: 'jasmine-expander-contents jasmine-stack-trace' }, stackTrace ) ); expandLink.addEventListener('click', function(e) { e.preventDefault(); if (root.classList.contains('jasmine-expanded')) { root.classList.remove('jasmine-expanded'); expandLink.textContent = 'Show stack trace'; } else { root.classList.add('jasmine-expanded'); expandLink.textContent = 'Hide stack trace'; } }); return root; } function find(selector) { return getContainer().querySelector('.jasmine_html-reporter ' + selector); } function clearPrior() { const oldReporter = find(''); if (oldReporter) { getContainer().removeChild(oldReporter); } } function createDom(type, attrs, childrenArrayOrVarArgs) { const el = createElement(type); let children; if (j$.isArray_(childrenArrayOrVarArgs)) { children = childrenArrayOrVarArgs; } else { children = []; for (let i = 2; i < arguments.length; i++) { children.push(arguments[i]); } } for (let i = 0; i < children.length; i++) { const child = children[i]; if (typeof child === 'string') { el.appendChild(createTextNode(child)); } else { if (child) { el.appendChild(child); } } } for (const attr in attrs) { if (attr == 'className') { el[attr] = attrs[attr]; } else { el.setAttribute(attr, attrs[attr]); } } return el; } function pluralize(singular, count) { const word = count == 1 ? singular : singular + 's'; return '' + count + ' ' + word; } function specHref(result) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('spec', result.fullName) ); } function seedHref(seed) { // include window.location.pathname to fix issue with karma-jasmine-html-reporter in angular: see https://github.com/jasmine/jasmine/issues/1906 return ( (window.location.pathname || '') + addToExistingQueryString('seed', seed) ); } function defaultQueryString(key, value) { return '?' + key + '=' + value; } function setMenuModeTo(mode) { htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode); } function noExpectations(result) { const allExpectations = result.failedExpectations.length + result.passedExpectations.length; return ( allExpectations === 0 && (result.status === 'passed' || result.status === 'failed') ); } function hasActiveSpec(resultNode) { if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') { return true; } if (resultNode.type == 'suite') { for (let i = 0, j = resultNode.children.length; i < j; i++) { if (hasActiveSpec(resultNode.children[i])) { return true; } } } } } return HtmlReporter; };