From 5f10b2e623c6c21d1b692bde4a8a5c6d4015cd44 Mon Sep 17 00:00:00 2001 From: Christian Williams & Ian Fisher Date: Thu, 1 Apr 2010 15:56:29 -0700 Subject: [PATCH] Added Env#versionString; nicer styling in TrivialReporter; hide passed and skipped tests by default, but allow them to be displayed. --- lib/TrivialReporter.js | 73 +++++++++++++++++--- lib/jasmine-0.10.2.js | 14 +++- lib/jasmine.css | 104 +++++++++++++++++++++++++---- spec/suites/EnvSpec.js | 19 +++++- spec/suites/TrivialReporterSpec.js | 30 +++++++-- src/Env.js | 12 ++++ 6 files changed, 223 insertions(+), 29 deletions(-) diff --git a/lib/TrivialReporter.js b/lib/TrivialReporter.js index 8219d4a..53d3f5e 100644 --- a/lib/TrivialReporter.js +++ b/lib/TrivialReporter.js @@ -17,7 +17,11 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA } for (var attr in attrs) { + if (attr == "className") { el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } } return el; @@ -26,10 +30,29 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { var suites = runner.suites(); - this.runnerDiv = this.createDom('div', { className: 'runner running' }, - this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), - this.runnerMessageSpan = this.createDom('span', {}, "Running...")); - this.document.body.appendChild(this.runnerDiv); + var showPassed, showSkipped; + + this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, + this.createDom('div', { className: 'banner' }, + this.createDom('div', { className: 'logo' }, + "Jasmine", + this.createDom('span', { className: 'version' }, runner.env.versionString())), + this.createDom('div', { className: 'options' }, + "Show ", + showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "), + showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }), + this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped") + ) + ), + + this.runnerDiv = this.createDom('div', { className: 'runner running' }, + this.createDom('a', { className: 'run_spec', href: '?' }, "run all"), + this.runnerMessageSpan = this.createDom('span', {}, "Running..."), + this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, "")) + ); + + this.document.body.appendChild(this.outerDiv); for (var i = 0; i < suites.length; i++) { var suite = suites[i]; @@ -37,7 +60,7 @@ jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"), this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description)); this.suiteDivs[suite.getFullName()] = suiteDiv; - var parentDiv = this.document.body; + var parentDiv = this.outerDiv; if (suite.parentSuite) { parentDiv = this.suiteDivs[suite.parentSuite.getFullName()]; } @@ -45,6 +68,23 @@ jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { } this.startedAt = new Date(); + + var self = this; + showPassed.onchange = function(evt) { + if (evt.target.checked) { + self.outerDiv.className += ' show-passed'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, ''); + } + }; + + showSkipped.onchange = function(evt) { + if (evt.target.checked) { + self.outerDiv.className += ' show-skipped'; + } else { + self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, ''); + } + }; }; jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { @@ -63,6 +103,8 @@ jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) { var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s"); message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"; this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild); + + this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString())); }; jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) { @@ -82,17 +124,30 @@ jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) { } var specDiv = this.createDom('div', { className: 'spec ' + status }, this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"), - this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, spec.getFullName())); + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(spec.getFullName()), + title: spec.getFullName() + }, spec.description)); var resultItems = results.getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); for (var i = 0; i < resultItems.length; i++) { var result = resultItems[i]; if (result.passed && !result.passed()) { - specDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); - specDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message)); + + if (result.trace.stack) { + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack)); + } } } + + if (messagesDiv.childNodes.length > 0) { + specDiv.appendChild(messagesDiv); + } + this.suiteDivs[spec.suite.getFullName()].appendChild(specDiv); }; @@ -114,4 +169,4 @@ jasmine.TrivialReporter.prototype.specFilter = function(spec) { if (!paramMap["spec"]) return true; return spec.getFullName().indexOf(paramMap["spec"]) == 0; -}; \ No newline at end of file +}; diff --git a/lib/jasmine-0.10.2.js b/lib/jasmine-0.10.2.js index 05af921..aeea270 100644 --- a/lib/jasmine-0.10.2.js +++ b/lib/jasmine-0.10.2.js @@ -690,6 +690,18 @@ jasmine.Env.prototype.version = function () { } }; +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (jasmine.version_) { + var version = this.version(); + return version.major + "." + version.minor + "." + version.build + " revision " + version.revision; + } else { + return "version unknown"; + } +}; + /** * @returns a sequential integer starting at 0 */ @@ -2314,5 +2326,5 @@ jasmine.version_= { "major": 0, "minor": 10, "build": 2, - "revision": 1268969696 + "revision": 1270162506 }; diff --git a/lib/jasmine.css b/lib/jasmine.css index 15927d9..6583fe7 100644 --- a/lib/jasmine.css +++ b/lib/jasmine.css @@ -3,8 +3,40 @@ body { } -body .run_spec { - float:right; +.jasmine_reporter a:visited, .jasmine_reporter a { + color: #303; +} + +.jasmine_reporter a:hover, .jasmine_reporter a:active { + color: blue; +} + +.run_spec { + float:right; + padding-right: 5px; + font-size: .8em; + text-decoration: none; +} + +.jasmine_reporter { + margin: 0 5px; +} + +.banner { + color: #303; + background-color: #fef; + padding: 5px; +} + +.logo { + float: left; + font-size: 1.1em; + padding-left: 5px; +} + +.logo .version { + font-size: .6em; + padding-left: 1em; } .runner.running { @@ -12,24 +44,26 @@ body .run_spec { } - -.runner { - border: 1px solid gray; - margin: 5px; - padding-left: 1em; - padding-right: 1em; +.options { + text-align: right; + font-size: .8em; } + .suite { border: 1px outset gray; - margin: 5px; + margin: 5px 0; padding-left: 1em; } +.suite .suite { + margin: 5px; +} + .suite.passed { - background-color: #cfc; + background-color: #dfd; } .suite.failed { @@ -38,22 +72,51 @@ body .run_spec { .spec { margin: 5px; + padding-left: 1em; clear: both; } +.spec.failed, .spec.passed, .spec.skipped { + padding-bottom: 5px; + border: 1px solid gray; +} + +.spec.failed { + background-color: #fbb; + border-color: red; +} + +.spec.passed { + background-color: #bfb; + border-color: green; +} + +.spec.skipped { + background-color: #bbb; +} + +.messages { + border-left: 1px dashed gray; + padding-left: 1em; + padding-right: 1em; +} + .passed { background-color: #cfc; + display: none; } .failed { - background-color: #fdd; + background-color: #fbb; } .skipped { color: #777; background-color: #eee; + display: none; } + /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ @@ -72,15 +135,32 @@ body .run_spec { white-space: pre; font-size: .8em; margin-left: 10px; - height: 5em; + max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +.finished-at { + padding-left: 1em; + font-size: .6em; +} + +.show-passed .passed, +.show-skipped .skipped { + display: block; +} + #jasmine_content { position:fixed; right: 100%; } + +.runner { + border: 1px solid gray; + display: block; + margin: 5px 0; + padding: 2px 0 2px 10px; +} diff --git a/spec/suites/EnvSpec.js b/spec/suites/EnvSpec.js index e907834..1b59f3e 100644 --- a/spec/suites/EnvSpec.js +++ b/spec/suites/EnvSpec.js @@ -57,6 +57,23 @@ describe("jasmine.Env", function() { "revision": 8 }); }); + + describe("versionString", function() { + it("should return a stringified version number", function() { + jasmine.version_ = { + "major": 1, + "minor": 9, + "build": 7, + "revision": 8 + }; + expect(env.versionString()).toEqual("1.9.7 revision 8"); + }); + + it("should return a nice string when version is unknown", function() { + jasmine.version_ = null; + expect(env.versionString()).toEqual("version unknown"); + }); + }); }); it("should allow reporters to be registered", function() { @@ -138,4 +155,4 @@ describe("jasmine.Env", function() { }); }); }); -}); \ No newline at end of file +}); diff --git a/spec/suites/TrivialReporterSpec.js b/spec/suites/TrivialReporterSpec.js index 2fcc2ca..a7ab433 100644 --- a/spec/suites/TrivialReporterSpec.js +++ b/spec/suites/TrivialReporterSpec.js @@ -14,6 +14,20 @@ describe("TrivialReporter", function() { }; } + function findElements(divs, withClass) { + var els = []; + for (var i = 0; i < divs.length; i++) { + if (divs[i].className == withClass) els.push(divs[i]); + } + return els; + } + + function findElement(divs, withClass) { + var els = findElements(divs, withClass); + if (els.length > 0) return els[0]; + throw new Error("couldn't find div with class " + withClass); + } + it("should run only specs beginning with spec parameter", function() { var trivialReporter = new jasmine.TrivialReporter({ location: {search: "?spec=run%20this"} }); expect(trivialReporter.specFilter(fakeSpec("run this"))).toBeTruthy(); @@ -24,14 +38,15 @@ describe("TrivialReporter", function() { it("should display empty divs for every suite when the runner is starting", function() { var trivialReporter = new jasmine.TrivialReporter({ body: body }); trivialReporter.reportRunnerStarting({ + env: new jasmine.Env(), suites: function() { return [ new jasmine.Suite({}, "suite 1", null, null) ]; } }); - var divs = body.getElementsByTagName("div"); - expect(divs.length).toEqual(2); - expect(divs[1].innerHTML).toContain("suite 1"); + var divs = findElements(body.getElementsByTagName("div"), "suite"); + expect(divs.length).toEqual(1); + expect(divs[0].innerHTML).toContain("suite 1"); }); describe('Matcher reporting', function () { @@ -104,6 +119,7 @@ describe("TrivialReporter", function() { trivialReporter = new jasmine.TrivialReporter({ body: body }); trivialReporter.reportRunnerStarting({ + env: new jasmine.Env(), suites: function() { return [ new jasmine.Suite({}, "suite 1", null, null) ]; } @@ -120,10 +136,11 @@ describe("TrivialReporter", function() { trivialReporter.reportSpecResults(spec); var divs = body.getElementsByTagName("div"); - expect(divs[3].innerHTML).toEqual("Expected 'a' to be null, but it was not"); + var errorDiv = findElement(divs, 'resultMessage fail'); + expect(errorDiv.innerHTML).toEqual("Expected 'a' to be null, but it was not"); }); - it("should add the failure message to the DOM (non-toEquals matchers)", function() { + it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() { expectationResult = new jasmine.ExpectationResult({ matcherName: "toBeNull", passed: false, message: "Expected '1 < 2' to e null, & it was not" }); @@ -133,7 +150,8 @@ describe("TrivialReporter", function() { trivialReporter.reportSpecResults(spec); var divs = body.getElementsByTagName("div"); - expect(divs[3].innerHTML).toEqual("Expected '1 < 2' to <b>e null, & it was not"); + var errorDiv = findElement(divs, 'resultMessage fail'); + expect(errorDiv.innerHTML).toEqual("Expected '1 < 2' to <b>e null, & it was not"); }); }); diff --git a/src/Env.js b/src/Env.js index 68304f7..94606b0 100644 --- a/src/Env.js +++ b/src/Env.js @@ -46,6 +46,18 @@ jasmine.Env.prototype.version = function () { } }; +/** + * @returns string containing jasmine version build info, if set. + */ +jasmine.Env.prototype.versionString = function() { + if (jasmine.version_) { + var version = this.version(); + return version.major + "." + version.minor + "." + version.build + " revision " + version.revision; + } else { + return "version unknown"; + } +}; + /** * @returns a sequential integer starting at 0 */