From 07996b567f83191e4258e131339c6ec0e8041b7b Mon Sep 17 00:00:00 2001 From: Elenore Bastian Date: Tue, 23 Jan 2018 15:39:11 -0800 Subject: [PATCH] Suite level errors are reported in failures list [#150118881] Signed-off-by: Gregg Van Hove --- lib/jasmine-core/jasmine-html.js | 256 ++++++++++++++++--------------- spec/html/HtmlReporterSpec.js | 201 ++++++++++++++---------- src/html/HtmlReporter.js | 252 +++++++++++++++--------------- src/html/ResultsNode.js | 4 + 4 files changed, 382 insertions(+), 331 deletions(-) diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 4e9558cd..3c54d040 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -49,9 +49,14 @@ jasmineRequire.HtmlReporter = function(j$) { }; 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) { @@ -86,10 +91,8 @@ jasmineRequire.HtmlReporter = function(j$) { addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, filterSpecs = options.filterSpecs, timer = options.timer || noopTimer, - results = [], htmlReporterMain, - symbols, - failedSuites = []; + symbols; this.initialize = function() { clearPrior(); @@ -122,11 +125,11 @@ jasmineRequire.HtmlReporter = function(j$) { }; this.suiteDone = function(result) { - if (result.status == 'failed') { - failedSuites.push(result); - } - stateBuilder.suiteDone(result); + + if (result.status === 'failed') { + failures.push(failureDom(result)); + } }; this.specStarted = function(result) { @@ -152,21 +155,8 @@ jasmineRequire.HtmlReporter = function(j$) { } )); - if (result.status == 'failed') { - var failure = - createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, - failureDescription(result, stateBuilder.currentParent), - createDom('div', {className: 'jasmine-messages'}) - ); - var messages = failure.childNodes[1]; - - for (var i = 0; i < result.failedExpectations.length; i++) { - var expectation = result.failedExpectations[i]; - messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); - messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); - } - - failures.push(failure); + if (result.status === 'failed') { + failures.push(failureDom(result)); } }; @@ -176,59 +166,7 @@ jasmineRequire.HtmlReporter = function(j$) { var order = doneResult && doneResult.order; alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); - banner.appendChild( - createDom('div', { className: 'jasmine-run-options' }, - createDom('span', { className: 'jasmine-trigger' }, 'Options'), - createDom('div', { className: 'jasmine-payload' }, - createDom('div', { className: 'jasmine-exceptions' }, - createDom('input', { - className: 'jasmine-raise', - id: 'jasmine-raise-exceptions', - type: 'checkbox' - }), - createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), - 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')) - ) - )); - - var raiseCheckbox = find('#jasmine-raise-exceptions'); - - raiseCheckbox.checked = !env.catchingExceptions(); - raiseCheckbox.onclick = onRaiseExceptionsClick; - - var throwCheckbox = find('#jasmine-throw-failures'); - throwCheckbox.checked = env.throwingExpectationFailures(); - throwCheckbox.onclick = onThrowExpectationsClick; - - var randomCheckbox = find('#jasmine-random-order'); - randomCheckbox.checked = env.randomTests(); - randomCheckbox.onclick = onRandomClick; - - var optionsMenu = find('.jasmine-run-options'), - optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'), - optionsPayload = optionsMenu.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'; - } - }; + banner.appendChild(optionsMenu(env)); if (stateBuilder.specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; @@ -242,7 +180,7 @@ jasmineRequire.HtmlReporter = function(j$) { var statusBarMessage = ''; var statusBarClassName = 'jasmine-overall-result jasmine-bar '; var globalFailures = (doneResult && doneResult.failedExpectations) || []; - var failed = stateBuilder.failureCount + globalFailures.length + failedSuites.length > 0; + var failed = stateBuilder.failureCount + globalFailures.length > 0; if (totalSpecsDefined > 0 || failed) { statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); @@ -271,13 +209,6 @@ jasmineRequire.HtmlReporter = function(j$) { var errorBarClassName = 'jasmine-bar jasmine-errored'; var afterAllMessagePrefix = 'AfterAll '; - for(var i = 0; i < failedSuites.length; i++) { - var failedSuite = failedSuites[i]; - for(var j = 0; j < failedSuite.failedExpectations.length; j++) { - alert.appendChild(createDom('span', {className: errorBarClassName}, afterAllMessagePrefix + failedSuite.failedExpectations[j].message)); - } - } - for(i = 0; i < globalFailures.length; i++) { alert.appendChild(createDom('span', {className: errorBarClassName}, globalFailureMessage(globalFailures[i]))); } @@ -301,47 +232,6 @@ jasmineRequire.HtmlReporter = function(j$) { summaryList(stateBuilder.topResults, summary); - function summaryList(resultsTree, domParent) { - var specListNode; - for (var i = 0; i < resultsTree.children.length; i++) { - var resultNode = resultsTree.children[i]; - if (filterSpecs && !hasActiveSpec(resultNode)) { - continue; - } - if (resultNode.type == 'suite') { - var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id}, - createDom('li', {className: 'jasmine-suite-detail'}, - 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); - } - var specDescription = resultNode.result.description; - if(noExpectations(resultNode.result)) { - specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; - } - if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { - specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; - } - specListNode.appendChild( - createDom('li', { - className: 'jasmine-' + resultNode.result.status, - id: 'spec-' + resultNode.result.id - }, - createDom('a', {href: specHref(resultNode.result)}, specDescription) - ) - ); - } - } - } - if (failures.length) { alert.appendChild( createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'}, @@ -370,6 +260,120 @@ jasmineRequire.HtmlReporter = function(j$) { return this; + function failureDom(result) { + var failure = + createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, + failureDescription(result, stateBuilder.currentParent), + createDom('div', {className: 'jasmine-messages'}) + ); + var messages = failure.childNodes[1]; + + for (var i = 0; i < result.failedExpectations.length; i++) { + var expectation = result.failedExpectations[i]; + messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); + messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); + } + + return failure; + } + + function summaryList(resultsTree, domParent) { + var specListNode; + for (var i = 0; i < resultsTree.children.length; i++) { + var resultNode = resultsTree.children[i]; + if (filterSpecs && !hasActiveSpec(resultNode)) { + continue; + } + if (resultNode.type === 'suite') { + var 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); + } + var specDescription = resultNode.result.description; + if(noExpectations(resultNode.result)) { + specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; + } + if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { + specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; + } + specListNode.appendChild( + createDom('li', { + className: 'jasmine-' + resultNode.result.status, + id: 'spec-' + resultNode.result.id + }, + createDom('a', {href: specHref(resultNode.result)}, specDescription) + ) + ); + } + } + } + + function optionsMenu(env) { + var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, + createDom('span', { className: 'jasmine-trigger' }, 'Options'), + createDom('div', { className: 'jasmine-payload' }, + createDom('div', { className: 'jasmine-exceptions' }, + createDom('input', { + className: 'jasmine-raise', + id: 'jasmine-raise-exceptions', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), + 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')) + ) + ); + + var raiseCheckbox = optionsMenuDom.querySelector('#jasmine-raise-exceptions'); + + raiseCheckbox.checked = !env.catchingExceptions(); + raiseCheckbox.onclick = onRaiseExceptionsClick; + + var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); + throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.onclick = onThrowExpectationsClick; + + var randomCheckbox = optionsMenuDom.querySelector('#jasmine-random-order'); + randomCheckbox.checked = env.randomTests(); + randomCheckbox.onclick = onRandomClick; + + var 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) { var wrapper = createDom('div', {className: 'jasmine-description'}, createDom('a', {title: result.description, href: specHref(result)}, result.description) @@ -510,6 +514,10 @@ jasmineRequire.ResultsNode = function() { this.last = function() { return this.children[this.children.length - 1]; }; + + this.updateResult = function(result) { + this.result = result; + }; } return ResultsNode; diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index 795a5ed9..45e1667a 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -177,69 +177,6 @@ describe("HtmlReporter", function() { }); }); - describe("when there are suite failures", function () { - it("displays the exceptions in their own alert bars", function(){ - var env = new jasmineUnderTest.Env(), - container = document.createElement("div"), - getContainer = function() { return container; }, - reporter = new jasmineUnderTest.HtmlReporter({ - env: env, - getContainer: getContainer, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); } - }); - - reporter.initialize(); - - reporter.jasmineStarted({}); - reporter.suiteDone({ status: 'failed', failedExpectations: [{ message: 'My After All Exception' }] }); - reporter.suiteDone({ status: 'failed', failedExpectations: [{ message: 'My Other Exception' }] }); - reporter.jasmineDone({ failedExpectations: [ - { message: 'Global After All Failure', globalErrorType: 'afterAll' }, - { message: 'Your JS is borken', globalErrorType: 'load' } - ] }); - - var alertBars = container.querySelectorAll(".jasmine-alert .jasmine-bar"); - - expect(alertBars.length).toEqual(5); - expect(alertBars[1].innerHTML).toMatch(/My After All Exception/); - expect(alertBars[1].getAttribute("class")).toEqual('jasmine-bar jasmine-errored'); - expect(alertBars[2].innerHTML).toMatch(/My Other Exception/); - expect(alertBars[3].innerHTML).toMatch(/AfterAll Global After All Failure/); - expect(alertBars[4].innerHTML).toMatch(/Error during loading: Your JS is borken/); - expect(alertBars[4].innerHTML).not.toMatch(/line/); - }); - - it("displays file and line information if available", function() { - var env = new jasmineUnderTest.Env(), - container = document.createElement("div"), - getContainer = function() { return container; }, - reporter = new jasmineUnderTest.HtmlReporter({ - env: env, - getContainer: getContainer, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); } - }); - - reporter.initialize(); - - reporter.jasmineStarted({}); - reporter.jasmineDone({ failedExpectations: [ - { - message: 'Your JS is borken', - globalErrorType: 'load', - filename: 'some/file.js', - lineno: 42 - } - ] }); - - var alertBars = container.querySelectorAll(".jasmine-alert .jasmine-bar"); - - expect(alertBars.length).toEqual(2); - expect(alertBars[1].innerHTML).toMatch(/Error during loading: Your JS is borken in some\/file.js line 42/); - }); - }); - describe("when Jasmine is done", function() { it("adds a warning to the link title of specs that have no expectations", function() { if (!window.console) { @@ -351,7 +288,12 @@ describe("HtmlReporter", function() { reporter.specStarted(specResult); reporter.specDone(specResult); - reporter.suiteDone({id: 2}); + reporter.suiteDone({ + id: 2, + status: 'things', + description: "inner suite", + fullName: "A Suite inner suite" + }); specResult = { id: 209, @@ -364,7 +306,12 @@ describe("HtmlReporter", function() { reporter.specStarted(specResult); reporter.specDone(specResult); - reporter.suiteDone({id: 1}); + reporter.suiteDone({ + id: 1, + status: 'things', + description: "A Suite", + fullName: "A Suite" + }); reporter.jasmineDone({}); var summary = container.querySelector(".jasmine-summary"); @@ -379,7 +326,7 @@ describe("HtmlReporter", function() { var node = outerSuite.childNodes[i]; classes.push(node.getAttribute("class")); } - expect(classes).toEqual(["jasmine-suite-detail", "jasmine-specs", "jasmine-suite", "jasmine-specs"]); + expect(classes).toEqual(["jasmine-suite-detail jasmine-things", "jasmine-specs", "jasmine-suite", "jasmine-specs"]); var suiteDetail = outerSuite.childNodes[0]; var suiteLink = suiteDetail.childNodes[0]; @@ -430,6 +377,65 @@ describe("HtmlReporter", function() { expect(payload).not.toHaveClass('jasmine-open'); }); + describe("when there are global errors", function() { + it("displays the exceptions in their own alert bars", function(){ + var env = new jasmineUnderTest.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + + reporter.jasmineStarted({}); + reporter.jasmineDone({ failedExpectations: [ + { message: 'Global After All Failure', globalErrorType: 'afterAll' }, + { message: 'Your JS is borken', globalErrorType: 'load' } + ] }); + + var alertBars = container.querySelectorAll(".jasmine-alert .jasmine-bar"); + + expect(alertBars.length).toEqual(3); + expect(alertBars[1].getAttribute("class")).toEqual('jasmine-bar jasmine-errored'); + expect(alertBars[1].innerHTML).toMatch(/AfterAll Global After All Failure/); + expect(alertBars[2].innerHTML).toMatch(/Error during loading: Your JS is borken/); + expect(alertBars[2].innerHTML).not.toMatch(/line/); + }); + + it("displays file and line information if available", function() { + var env = new jasmineUnderTest.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmineUnderTest.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + + reporter.initialize(); + + reporter.jasmineStarted({}); + reporter.jasmineDone({ failedExpectations: [ + { + message: 'Your JS is borken', + globalErrorType: 'load', + filename: 'some/file.js', + lineno: 42 + } + ] }); + + var alertBars = container.querySelectorAll(".jasmine-alert .jasmine-bar"); + + expect(alertBars.length).toEqual(2); + expect(alertBars[1].innerHTML).toMatch(/Error during loading: Your JS is borken in some\/file.js line 42/); + }); + }); + describe("UI for raising/catching exceptions", function() { it("should be unchecked if the env is catching", function() { var env = new jasmineUnderTest.Env(), @@ -928,11 +934,11 @@ describe("HtmlReporter", function() { description: "inner suite" }); - var passingResult = {id: 123, status: "passed", passedExpectations: [{passed: true}], failedExpectations: []}; - reporter.specStarted(passingResult); - reporter.specDone(passingResult); + var passingSpecResult = {id: 123, status: "passed", passedExpectations: [{passed: true}], failedExpectations: []}; + reporter.specStarted(passingSpecResult); + reporter.specDone(passingSpecResult); - var failingResult = { + var failingSpecResult = { id: 124, status: "failed", description: "a failing spec", @@ -945,36 +951,65 @@ describe("HtmlReporter", function() { } ] }; - reporter.specStarted(failingResult); - reporter.specDone(failingResult); - reporter.suiteDone({}); - reporter.suiteDone({}); - reporter.suiteDone({}); + + var passingSuiteResult = { + id: 1, + description: "A suite" + }; + var failingSuiteResult = { + id: 2, + description: 'a suite', + fullName: 'a suite', + status: 'failed', + failedExpectations: [{ message: 'My After All Exception' }] + }; + reporter.specStarted(failingSpecResult); + reporter.specDone(failingSpecResult); + reporter.suiteDone(passingSuiteResult); + reporter.suiteDone(failingSuiteResult); + reporter.suiteDone(passingSuiteResult); reporter.jasmineDone({}); }); it("reports the specs counts", function() { var alertBar = container.querySelector(".jasmine-alert .jasmine-bar"); - expect(alertBar.innerHTML).toMatch(/2 specs, 1 failure/); + expect(alertBar.innerHTML).toMatch(/2 specs, 2 failure/); }); it("reports failure messages and stack traces", function() { var specFailures = container.querySelector(".jasmine-failures"); - var failure = specFailures.childNodes[0]; - expect(failure.getAttribute("class")).toMatch(/jasmine-failed/); - expect(failure.getAttribute("class")).toMatch(/jasmine-spec-detail/); + expect(specFailures.childNodes.length).toEqual(2); - var specDiv = failure.childNodes[0]; + var specFailure = specFailures.childNodes[0]; + expect(specFailure.getAttribute("class")).toMatch(/jasmine-failed/); + expect(specFailure.getAttribute("class")).toMatch(/jasmine-spec-detail/); + + var specDiv = specFailure.childNodes[0]; expect(specDiv.getAttribute("class")).toEqual("jasmine-description"); - var message = failure.childNodes[1].childNodes[0]; + var message = specFailure.childNodes[1].childNodes[0]; expect(message.getAttribute("class")).toEqual("jasmine-result-message"); expect(message.innerHTML).toEqual("a failure message"); - var stackTrace = failure.childNodes[1].childNodes[1]; + var stackTrace = specFailure.childNodes[1].childNodes[1]; expect(stackTrace.getAttribute("class")).toEqual("jasmine-stack-trace"); expect(stackTrace.innerHTML).toEqual("a stack trace"); + + var suiteFailure = specFailures.childNodes[0]; + expect(suiteFailure.getAttribute("class")).toMatch(/jasmine-failed/); + expect(suiteFailure.getAttribute("class")).toMatch(/jasmine-spec-detail/); + + var suiteDiv = suiteFailure.childNodes[0]; + expect(suiteDiv.getAttribute("class")).toEqual("jasmine-description"); + + var suiteMessage = suiteFailure.childNodes[1].childNodes[0]; + expect(suiteMessage.getAttribute("class")).toEqual("jasmine-result-message"); + expect(suiteMessage.innerHTML).toEqual("a failure message"); + + var suiteStackTrace = suiteFailure.childNodes[1].childNodes[1]; + expect(suiteStackTrace.getAttribute("class")).toEqual("jasmine-stack-trace"); + expect(suiteStackTrace.innerHTML).toEqual("a stack trace"); }); it('provides links to focus on a failure and each containing suite', function() { @@ -1063,7 +1098,7 @@ describe("HtmlReporter", function() { }); }); - describe("When the jasmineDone event's overallStatus is 'failed'", function() { + describe("When the jasmineDone event's overallStatus is 'incomplete'", function() { it("has class jasmine-incomplete", function() { var env = new jasmineUnderTest.Env(), container = document.createElement("div"), diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 72d4ee78..1dfde539 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -19,9 +19,14 @@ jasmineRequire.HtmlReporter = function(j$) { }; 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) { @@ -56,10 +61,8 @@ jasmineRequire.HtmlReporter = function(j$) { addToExistingQueryString = options.addToExistingQueryString || defaultQueryString, filterSpecs = options.filterSpecs, timer = options.timer || noopTimer, - results = [], htmlReporterMain, - symbols, - failedSuites = []; + symbols; this.initialize = function() { clearPrior(); @@ -92,11 +95,11 @@ jasmineRequire.HtmlReporter = function(j$) { }; this.suiteDone = function(result) { - if (result.status == 'failed') { - failedSuites.push(result); - } - stateBuilder.suiteDone(result); + + if (result.status === 'failed') { + failures.push(failureDom(result)); + } }; this.specStarted = function(result) { @@ -122,21 +125,8 @@ jasmineRequire.HtmlReporter = function(j$) { } )); - if (result.status == 'failed') { - var failure = - createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, - failureDescription(result, stateBuilder.currentParent), - createDom('div', {className: 'jasmine-messages'}) - ); - var messages = failure.childNodes[1]; - - for (var i = 0; i < result.failedExpectations.length; i++) { - var expectation = result.failedExpectations[i]; - messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); - messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); - } - - failures.push(failure); + if (result.status === 'failed') { + failures.push(failureDom(result)); } }; @@ -146,59 +136,7 @@ jasmineRequire.HtmlReporter = function(j$) { var order = doneResult && doneResult.order; alert.appendChild(createDom('span', {className: 'jasmine-duration'}, 'finished in ' + timer.elapsed() / 1000 + 's')); - banner.appendChild( - createDom('div', { className: 'jasmine-run-options' }, - createDom('span', { className: 'jasmine-trigger' }, 'Options'), - createDom('div', { className: 'jasmine-payload' }, - createDom('div', { className: 'jasmine-exceptions' }, - createDom('input', { - className: 'jasmine-raise', - id: 'jasmine-raise-exceptions', - type: 'checkbox' - }), - createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), - 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')) - ) - )); - - var raiseCheckbox = find('#jasmine-raise-exceptions'); - - raiseCheckbox.checked = !env.catchingExceptions(); - raiseCheckbox.onclick = onRaiseExceptionsClick; - - var throwCheckbox = find('#jasmine-throw-failures'); - throwCheckbox.checked = env.throwingExpectationFailures(); - throwCheckbox.onclick = onThrowExpectationsClick; - - var randomCheckbox = find('#jasmine-random-order'); - randomCheckbox.checked = env.randomTests(); - randomCheckbox.onclick = onRandomClick; - - var optionsMenu = find('.jasmine-run-options'), - optionsTrigger = optionsMenu.querySelector('.jasmine-trigger'), - optionsPayload = optionsMenu.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'; - } - }; + banner.appendChild(optionsMenu(env)); if (stateBuilder.specsExecuted < totalSpecsDefined) { var skippedMessage = 'Ran ' + stateBuilder.specsExecuted + ' of ' + totalSpecsDefined + ' specs - run all'; @@ -212,7 +150,7 @@ jasmineRequire.HtmlReporter = function(j$) { var statusBarMessage = ''; var statusBarClassName = 'jasmine-overall-result jasmine-bar '; var globalFailures = (doneResult && doneResult.failedExpectations) || []; - var failed = stateBuilder.failureCount + globalFailures.length + failedSuites.length > 0; + var failed = stateBuilder.failureCount + globalFailures.length > 0; if (totalSpecsDefined > 0 || failed) { statusBarMessage += pluralize('spec', stateBuilder.specsExecuted) + ', ' + pluralize('failure', stateBuilder.failureCount); @@ -241,13 +179,6 @@ jasmineRequire.HtmlReporter = function(j$) { var errorBarClassName = 'jasmine-bar jasmine-errored'; var afterAllMessagePrefix = 'AfterAll '; - for(var i = 0; i < failedSuites.length; i++) { - var failedSuite = failedSuites[i]; - for(var j = 0; j < failedSuite.failedExpectations.length; j++) { - alert.appendChild(createDom('span', {className: errorBarClassName}, afterAllMessagePrefix + failedSuite.failedExpectations[j].message)); - } - } - for(i = 0; i < globalFailures.length; i++) { alert.appendChild(createDom('span', {className: errorBarClassName}, globalFailureMessage(globalFailures[i]))); } @@ -271,47 +202,6 @@ jasmineRequire.HtmlReporter = function(j$) { summaryList(stateBuilder.topResults, summary); - function summaryList(resultsTree, domParent) { - var specListNode; - for (var i = 0; i < resultsTree.children.length; i++) { - var resultNode = resultsTree.children[i]; - if (filterSpecs && !hasActiveSpec(resultNode)) { - continue; - } - if (resultNode.type == 'suite') { - var suiteListNode = createDom('ul', {className: 'jasmine-suite', id: 'suite-' + resultNode.result.id}, - createDom('li', {className: 'jasmine-suite-detail'}, - 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); - } - var specDescription = resultNode.result.description; - if(noExpectations(resultNode.result)) { - specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; - } - if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { - specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; - } - specListNode.appendChild( - createDom('li', { - className: 'jasmine-' + resultNode.result.status, - id: 'spec-' + resultNode.result.id - }, - createDom('a', {href: specHref(resultNode.result)}, specDescription) - ) - ); - } - } - } - if (failures.length) { alert.appendChild( createDom('span', {className: 'jasmine-menu jasmine-bar jasmine-spec-list'}, @@ -340,6 +230,120 @@ jasmineRequire.HtmlReporter = function(j$) { return this; + function failureDom(result) { + var failure = + createDom('div', {className: 'jasmine-spec-detail jasmine-failed'}, + failureDescription(result, stateBuilder.currentParent), + createDom('div', {className: 'jasmine-messages'}) + ); + var messages = failure.childNodes[1]; + + for (var i = 0; i < result.failedExpectations.length; i++) { + var expectation = result.failedExpectations[i]; + messages.appendChild(createDom('div', {className: 'jasmine-result-message'}, expectation.message)); + messages.appendChild(createDom('div', {className: 'jasmine-stack-trace'}, expectation.stack)); + } + + return failure; + } + + function summaryList(resultsTree, domParent) { + var specListNode; + for (var i = 0; i < resultsTree.children.length; i++) { + var resultNode = resultsTree.children[i]; + if (filterSpecs && !hasActiveSpec(resultNode)) { + continue; + } + if (resultNode.type === 'suite') { + var 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); + } + var specDescription = resultNode.result.description; + if(noExpectations(resultNode.result)) { + specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription; + } + if(resultNode.result.status === 'pending' && resultNode.result.pendingReason !== '') { + specDescription = specDescription + ' PENDING WITH MESSAGE: ' + resultNode.result.pendingReason; + } + specListNode.appendChild( + createDom('li', { + className: 'jasmine-' + resultNode.result.status, + id: 'spec-' + resultNode.result.id + }, + createDom('a', {href: specHref(resultNode.result)}, specDescription) + ) + ); + } + } + } + + function optionsMenu(env) { + var optionsMenuDom = createDom('div', { className: 'jasmine-run-options' }, + createDom('span', { className: 'jasmine-trigger' }, 'Options'), + createDom('div', { className: 'jasmine-payload' }, + createDom('div', { className: 'jasmine-exceptions' }, + createDom('input', { + className: 'jasmine-raise', + id: 'jasmine-raise-exceptions', + type: 'checkbox' + }), + createDom('label', { className: 'jasmine-label', 'for': 'jasmine-raise-exceptions' }, 'raise exceptions')), + 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')) + ) + ); + + var raiseCheckbox = optionsMenuDom.querySelector('#jasmine-raise-exceptions'); + + raiseCheckbox.checked = !env.catchingExceptions(); + raiseCheckbox.onclick = onRaiseExceptionsClick; + + var throwCheckbox = optionsMenuDom.querySelector('#jasmine-throw-failures'); + throwCheckbox.checked = env.throwingExpectationFailures(); + throwCheckbox.onclick = onThrowExpectationsClick; + + var randomCheckbox = optionsMenuDom.querySelector('#jasmine-random-order'); + randomCheckbox.checked = env.randomTests(); + randomCheckbox.onclick = onRandomClick; + + var 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) { var wrapper = createDom('div', {className: 'jasmine-description'}, createDom('a', {title: result.description, href: specHref(result)}, result.description) diff --git a/src/html/ResultsNode.js b/src/html/ResultsNode.js index 24cea254..bc3e0b4c 100644 --- a/src/html/ResultsNode.js +++ b/src/html/ResultsNode.js @@ -13,6 +13,10 @@ jasmineRequire.ResultsNode = function() { this.last = function() { return this.children[this.children.length - 1]; }; + + this.updateResult = function(result) { + this.result = result; + }; } return ResultsNode;