diff --git a/.gitignore b/.gitignore index 03ef271e..94c9e2aa 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ site/ tags Gemfile.lock pkg/* +.sass-cache/* +src/html/.sass-cache/* \ No newline at end of file diff --git a/config.rb b/config.rb new file mode 100644 index 00000000..fc529ba0 --- /dev/null +++ b/config.rb @@ -0,0 +1,29 @@ +# +# Compass configuration file - for building Jasmine's 'final CSS files +# + +# Require any additional compass plugins here. + +# Set this to the root of your project when deployed: +http_path = "/" +css_dir = "src/html" +sass_dir = "src/html" +images_dir = "images" +javascripts_dir = "javascripts" + +# You can select your preferred output style here (can be overridden via the command line): +# output_style = :expanded or :nested or :compact or :compressed +output_style = :compact + +# To enable relative paths to assets via compass helper functions. Uncomment: +# relative_assets = true + +# To disable debugging comments that display the original location of your selectors. Uncomment: +line_comments = false + + +# If you prefer the indented syntax, you might want to regenerate this +# project again passing --syntax sass, or you can uncomment this: +# preferred_syntax = :sass +# and then run: +# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass diff --git a/jasmine-core.gemspec b/jasmine-core.gemspec index c4dab4d0..1d834f8b 100644 --- a/jasmine-core.gemspec +++ b/jasmine-core.gemspec @@ -18,5 +18,7 @@ Gem::Specification.new do |s| s.add_development_dependency "term-ansicolor" s.add_development_dependency "json_pure", ">= 1.4.3" s.add_development_dependency "frank" + s.add_development_dependency "sass" + s.add_development_dependency "compass" s.add_development_dependency "ragaskar-jsdoc_helper" end diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 73834010..3de4e8a5 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -1,3 +1,489 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = doc.location.search.substring(1).split('&'); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + +jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + 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) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + +/* @deprecated Use jasmine.HtmlReporter instead + */ jasmine.TrivialReporter = function(doc) { this.document = doc || document; this.suiteDivs = {}; @@ -31,7 +517,7 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { var showPassed, showSkipped; - this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, this.createDom('div', { className: 'banner' }, this.createDom('div', { className: 'logo' }, this.createDom('span', { className: 'title' }, "Jasmine"), diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index 6583fe7c..826e5753 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -1,166 +1,81 @@ -body { - font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; -} +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } -.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 { - background-color: yellow; -} - - -.options { - text-align: right; - font-size: .8em; -} - - - - -.suite { - border: 1px outset gray; - margin: 5px 0; - padding-left: 1em; -} - -.suite .suite { - margin: 5px; -} - -.suite.passed { - background-color: #dfd; -} - -.suite.failed { - background-color: #fdd; -} - -.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: #fbb; -} - -.skipped { - color: #777; - background-color: #eee; - display: none; -} - - -/*.resultMessage {*/ - /*white-space: pre;*/ -/*}*/ - -.resultMessage span.result { - display: block; - line-height: 2em; - color: black; -} - -.resultMessage .mismatch { - color: black; -} - -.stackTrace { - white-space: pre; - font-size: .8em; - margin-left: 10px; - 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; -} +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index c3d2dc7d..8bba9262 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -2472,5 +2472,5 @@ jasmine.version_= { "major": 1, "minor": 1, "build": 0, - "revision": 1315677058 + "revision": 1320442951 }; diff --git a/spec/html/HTMLReporterSpec.js b/spec/html/HTMLReporterSpec.js new file mode 100644 index 00000000..84ee3a96 --- /dev/null +++ b/spec/html/HTMLReporterSpec.js @@ -0,0 +1,194 @@ +describe("HtmlReporter", function() { + var env; + var htmlReporter; + var body; + var fakeDocument; + + beforeEach(function() { + env = new jasmine.Env(); + env.updateInterval = 0; + + body = document.createElement("body"); + fakeDocument = { body: body, location: { search: "" } }; + htmlReporter = new jasmine.HtmlReporter(fakeDocument); + }); + + function fakeSpec(name) { + return { + getFullName: function() { + return name; + } + }; + } + + 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() { + fakeDocument.location.search = "?spec=run%20this"; + expect(htmlReporter.specFilter(fakeSpec("run this"))).toBeTruthy(); + expect(htmlReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy(); + expect(htmlReporter.specFilter(fakeSpec("not run this"))).toBeFalsy(); + }); + + describe('Matcher reporting', function () { + var getResultMessageDiv = function (body) { + var divs = body.getElementsByTagName("div"); + for (var i = 0; i < divs.length; i++) { + if (divs[i].className.match(/resultMessage/)) { + return divs[i]; + } + } + }; + + var runner, spec, fakeTimer; + beforeEach(function () { + fakeTimer = new jasmine.FakeTimer(); + env.setTimeout = fakeTimer.setTimeout; + env.clearTimeout = fakeTimer.clearTimeout; + env.setInterval = fakeTimer.setInterval; + env.clearInterval = fakeTimer.clearInterval; + runner = env.currentRunner(); + var suite = new jasmine.Suite(env, 'some suite'); + runner.add(suite); + spec = new jasmine.Spec(env, suite, 'some spec'); + suite.add(spec); + fakeDocument.location.search = "?"; + env.addReporter(htmlReporter); + }); + + describe('toContain', function () { + it('should show actual and expected', function () { + spec.runs(function () { + this.expect('foo').toContain('bar'); + }); + runner.execute(); + fakeTimer.tick(0); + + var resultEl = getResultMessageDiv(body); + expect(resultEl.innerHTML).toMatch(/foo/); + expect(resultEl.innerHTML).toMatch(/bar/); + }); + }); + }); + + describe("failure messages (integration)", function () { + var spec, results, expectationResult; + + it("should add the failure message to the DOM (non-toEquals matchers)", function() { + env.describe("suite", function() { + env.it("will have log messages", function() { + this.expect('a').toBeNull(); + }); + }); + + env.addReporter(htmlReporter); + env.execute(); + + var divs = body.getElementsByTagName("div"); + var errorDiv = findElement(divs, 'resultMessage fail'); + expect(errorDiv.innerHTML).toMatch(/Expected 'a' to be null/); + }); + + it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() { + env.describe("suite", function() { + env.it("will have log messages", function() { + this.expect('1 < 2').toBeNull(); + }); + }); + + env.addReporter(htmlReporter); + env.execute(); + + var divs = body.getElementsByTagName("div"); + var errorDiv = findElement(divs, 'resultMessage fail'); + expect(errorDiv.innerHTML).toMatch(/Expected '1 < 2' to be null/); + }); + }); + + describe("log messages", function() { + it("should appear in the report of a failed spec", function() { + env.describe("suite", function() { + env.it("will have log messages", function() { + this.log("this is a", "multipart log message"); + this.expect(true).toBeFalsy(); + }); + }); + + env.addReporter(htmlReporter); + env.execute(); + + var divs = body.getElementsByTagName("div"); + var errorDiv = findElement(divs, 'specDetail failed'); + expect(errorDiv.innerHTML).toMatch("this is a multipart log message"); + }); + + xit("should work on IE without console.log.apply", function() { + }); + }); + + describe("duplicate example names", function() { + it("should report failures correctly", function() { + var suite1 = env.describe("suite", function() { + env.it("will have log messages", function() { + this.log("this one fails!"); + this.expect(true).toBeFalsy(); + }); + }); + + var suite2 = env.describe("suite", function() { + env.it("will have log messages", function() { + this.log("this one passes!"); + this.expect(true).toBeTruthy(); + }); + }); + + env.addReporter(htmlReporter); + env.execute(); + + var divs = body.getElementsByTagName("div"); + var failedSpecDiv = findElement(divs, 'specDetail failed'); + expect(failedSpecDiv.className).toEqual('specDetail failed'); + expect(failedSpecDiv.innerHTML).toContain("this one fails!"); + expect(failedSpecDiv.innerHTML).not.toContain("this one passes!"); + }); + }); + + describe('#reportSpecStarting', function() { + beforeEach(function () { + env.describe("suite 1", function() { + env.it("spec 1", function() { + }); + }); + spyOn(htmlReporter, 'log').andCallThrough(); + }); + + it('DOES NOT log running specs by default', function() { + env.addReporter(htmlReporter); + env.execute(); + + expect(htmlReporter.log).not.toHaveBeenCalled(); + }); + + it('logs running specs when log_running_specs is true', function() { + htmlReporter.logRunningSpecs = true; + env.addReporter(htmlReporter); + env.execute(); + + expect(htmlReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...'); + }); + }); +}); diff --git a/spec/runner.html b/spec/runner.html index 3d5dfe34..0cde61fb 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -15,6 +15,12 @@ + + + + + + @@ -38,6 +44,7 @@ + @@ -48,12 +55,12 @@ var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; - var trivialReporter = new jasmine.TrivialReporter(); + var htmlReporter = new jasmine.HtmlReporter(); - jasmineEnv.addReporter(trivialReporter); + jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function(spec) { - return trivialReporter.specFilter(spec); + return htmlReporter.specFilter(spec); }; var currentWindowOnload = window.onload; diff --git a/spec/templates/runner.html.erb b/spec/templates/runner.html.erb index 964e1682..a4aceedd 100644 --- a/spec/templates/runner.html.erb +++ b/spec/templates/runner.html.erb @@ -18,12 +18,12 @@ var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; - var trivialReporter = new jasmine.TrivialReporter(); + var htmlReporter = new jasmine.HtmlReporter(); - jasmineEnv.addReporter(trivialReporter); + jasmineEnv.addReporter(htmlReporter); jasmineEnv.specFilter = function(spec) { - return trivialReporter.specFilter(spec); + return htmlReporter.specFilter(spec); }; var currentWindowOnload = window.onload; diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js new file mode 100644 index 00000000..c70a9a1e --- /dev/null +++ b/src/html/HtmlReporter.js @@ -0,0 +1,101 @@ +jasmine.HtmlReporter = function(_doc) { + var self = this; + var doc = _doc || window.document; + + var reporterView; + + var dom = {}; + + // Jasmine Reporter Public Interface + self.logRunningSpecs = false; + + self.reportRunnerStarting = function(runner) { + var specs = runner.specs() || []; + + if (specs.length == 0) { + return; + } + + createReporterDom(runner.env.versionString()); + doc.body.appendChild(dom.reporter); + + reporterView = new jasmine.HtmlReporter.ReporterView(dom); + reporterView.addSpecs(specs, self.specFilter); + }; + + self.reportRunnerResults = function(runner) { + reporterView.complete(); + }; + + self.reportSuiteResults = function(suite) { + reporterView.suiteComplete(suite); + }; + + self.reportSpecStarting = function(spec) { + if (self.logRunningSpecs) { + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...'); + } + }; + + self.reportSpecResults = function(spec) { + reporterView.specComplete(spec); + }; + + self.log = function() { + var console = jasmine.getGlobal().console; + if (console && console.log) { + if (console.log.apply) { + console.log.apply(console, arguments); + } else { + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie + } + } + }; + + self.specFilter = function(spec) { + if (!focusedSpecName()) { + return true; + } + + return spec.getFullName().indexOf(focusedSpecName()) === 0; + }; + + return self; + + function focusedSpecName() { + var specName; + + (function memoizeFocusedSpec() { + if (specName) { + return; + } + + var paramMap = []; + var params = doc.location.search.substring(1).split('&'); + + for (var i = 0; i < params.length; i++) { + var p = params[i].split('='); + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]); + } + + specName = paramMap.spec; + })(); + + return specName; + } + + function createReporterDom(version) { + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' }, + dom.banner = self.createDom('div', { className: 'banner' }, + self.createDom('span', { className: 'title' }, "Jasmine "), + self.createDom('span', { className: 'version' }, version)), + + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}), + dom.alert = self.createDom('div', {className: 'alert'}), + dom.results = self.createDom('div', {className: 'results'}, + dom.summary = self.createDom('div', { className: 'summary' }), + dom.details = self.createDom('div', { id: 'details' })) + ); + } +}; +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter); \ No newline at end of file diff --git a/src/html/HtmlReporterHelpers.js b/src/html/HtmlReporterHelpers.js new file mode 100644 index 00000000..745e1e09 --- /dev/null +++ b/src/html/HtmlReporterHelpers.js @@ -0,0 +1,60 @@ +jasmine.HtmlReporterHelpers = {}; + +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) { + var el = document.createElement(type); + + for (var i = 2; i < arguments.length; i++) { + var child = arguments[i]; + + if (typeof child === 'string') { + el.appendChild(document.createTextNode(child)); + } else { + if (child) { + el.appendChild(child); + } + } + } + + for (var attr in attrs) { + if (attr == "className") { + el[attr] = attrs[attr]; + } else { + el.setAttribute(attr, attrs[attr]); + } + } + + return el; +}; + +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) { + var results = child.results(); + var status = results.passed() ? 'passed' : 'failed'; + if (results.skipped) { + status = 'skipped'; + } + + return status; +}; + +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) { + var parentDiv = this.dom.summary; + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite'; + var parent = child[parentSuite]; + + if (parent) { + if (typeof this.views.suites[parent.id] == 'undefined') { + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views); + } + parentDiv = this.views.suites[parent.id].element; + } + + parentDiv.appendChild(childElement); +}; + + +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) { + for(var fn in jasmine.HtmlReporterHelpers) { + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn]; + } +}; + diff --git a/src/html/ReporterView.js b/src/html/ReporterView.js new file mode 100644 index 00000000..6a6d0056 --- /dev/null +++ b/src/html/ReporterView.js @@ -0,0 +1,164 @@ +jasmine.HtmlReporter.ReporterView = function(dom) { + this.startedAt = new Date(); + this.runningSpecCount = 0; + this.completeSpecCount = 0; + this.passedCount = 0; + this.failedCount = 0; + this.skippedCount = 0; + + this.createResultsMenu = function() { + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'}, + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'), + ' | ', + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing')); + + this.summaryMenuItem.onclick = function() { + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, ''); + }; + + this.detailsMenuItem.onclick = function() { + showDetails(); + }; + }; + + this.addSpecs = function(specs, specFilter) { + this.totalSpecCount = specs.length; + + this.views = { + specs: {}, + suites: {} + }; + + for (var i = 0; i < specs.length; i++) { + var spec = specs[i]; + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views); + if (specFilter(spec)) { + this.runningSpecCount++; + } + } + }; + + this.specComplete = function(spec) { + this.completeSpecCount++; + + if (isUndefined(this.views.specs[spec.id])) { + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom); + } + + var specView = this.views.specs[spec.id]; + + switch (specView.status()) { + case 'passed': + this.passedCount++; + break; + + case 'failed': + this.failedCount++; + break; + + case 'skipped': + this.skippedCount++; + break; + } + + specView.refresh(); + this.refresh(); + }; + + this.suiteComplete = function(suite) { + var suiteView = this.views.suites[suite.id]; + if (isUndefined(suiteView)) { + return; + } + suiteView.refresh(); + }; + + this.refresh = function() { + + if (isUndefined(this.resultsMenu)) { + this.createResultsMenu(); + } + + // currently running UI + if (isUndefined(this.runningAlert)) { + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"}); + dom.alert.appendChild(this.runningAlert); + } + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount); + + // skipped specs UI + if (isUndefined(this.skippedAlert)) { + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"}); + } + + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.skippedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.skippedAlert); + } + + // passing specs UI + if (isUndefined(this.passedAlert)) { + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"}); + } + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount); + + // failing specs UI + if (isUndefined(this.failedAlert)) { + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"}); + } + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount); + + if (this.failedCount === 1 && isDefined(dom.alert)) { + dom.alert.appendChild(this.failedAlert); + dom.alert.appendChild(this.resultsMenu); + } + + // summary info + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount); + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing"; + }; + + this.complete = function() { + dom.alert.removeChild(this.runningAlert); + + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all"; + + if (this.failedCount === 0) { + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount))); + } else { + showDetails(); + } + + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s")); + }; + + return this; + + function showDetails() { + if (dom.reporter.className.search(/showDetails/) === -1) { + dom.reporter.className += " showDetails"; + } + } + + function isUndefined(obj) { + return typeof obj === 'undefined'; + } + + function isDefined(obj) { + return !isUndefined(obj); + } + + function specPluralizedFor(count) { + var str = count + " spec"; + if (count > 1) { + str += "s" + } + return str; + } + +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView); + + diff --git a/src/html/SpecView.js b/src/html/SpecView.js new file mode 100644 index 00000000..8769bb84 --- /dev/null +++ b/src/html/SpecView.js @@ -0,0 +1,79 @@ +jasmine.HtmlReporter.SpecView = function(spec, dom, views) { + this.spec = spec; + this.dom = dom; + this.views = views; + + this.symbol = this.createDom('li', { className: 'pending' }); + this.dom.symbolSummary.appendChild(this.symbol); + + this.summary = this.createDom('div', { className: 'specSummary' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.description) + ); + + this.detail = this.createDom('div', { className: 'specDetail' }, + this.createDom('a', { + className: 'description', + href: '?spec=' + encodeURIComponent(this.spec.getFullName()), + title: this.spec.getFullName() + }, this.spec.getFullName()) + ); +}; + +jasmine.HtmlReporter.SpecView.prototype.status = function() { + return this.getSpecStatus(this.spec); +}; + +jasmine.HtmlReporter.SpecView.prototype.refresh = function() { + this.symbol.className = this.status(); + + switch (this.status()) { + case 'skipped': + break; + + case 'passed': + this.appendSummaryToSuiteDiv(); + break; + + case 'failed': + this.appendSummaryToSuiteDiv(); + this.appendFailureDetail(); + break; + } +}; + +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() { + this.summary.className += ' ' + this.status(); + this.appendToSummary(this.spec, this.summary); +}; + +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() { + this.detail.className += ' ' + this.status(); + + var resultItems = this.spec.results().getItems(); + var messagesDiv = this.createDom('div', { className: 'messages' }); + + for (var i = 0; i < resultItems.length; i++) { + var result = resultItems[i]; + + if (result.type == 'log') { + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString())); + } else if (result.type == 'expect' && result.passed && !result.passed()) { + 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) { + this.detail.appendChild(messagesDiv); + this.dom.details.appendChild(this.detail); + } +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView); \ No newline at end of file diff --git a/src/html/SuiteView.js b/src/html/SuiteView.js new file mode 100644 index 00000000..19a1efaf --- /dev/null +++ b/src/html/SuiteView.js @@ -0,0 +1,22 @@ +jasmine.HtmlReporter.SuiteView = function(suite, dom, views) { + this.suite = suite; + this.dom = dom; + this.views = views; + + this.element = this.createDom('div', { className: 'suite' }, + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description) + ); + + this.appendToSummary(this.suite, this.element); +}; + +jasmine.HtmlReporter.SuiteView.prototype.status = function() { + return this.getSpecStatus(this.suite); +}; + +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() { + this.element.className += " " + this.status(); +}; + +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView); + diff --git a/src/html/TrivialReporter.js b/src/html/TrivialReporter.js index 73834010..167ac506 100644 --- a/src/html/TrivialReporter.js +++ b/src/html/TrivialReporter.js @@ -1,3 +1,5 @@ +/* @deprecated Use jasmine.HtmlReporter instead + */ jasmine.TrivialReporter = function(doc) { this.document = doc || document; this.suiteDivs = {}; @@ -31,7 +33,7 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) { var showPassed, showSkipped; - this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' }, + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' }, this.createDom('div', { className: 'banner' }, this.createDom('div', { className: 'logo' }, this.createDom('span', { className: 'title' }, "Jasmine"), diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss new file mode 100644 index 00000000..8abbddf2 --- /dev/null +++ b/src/html/_HTMLReporter.scss @@ -0,0 +1,302 @@ +@import "compass"; + +$line-height: 14px; +$margin-unit: 14px; + +$feint-text-color: #aaa; +$light-text-color: #666; +$text-color: #333; + +$page-background-color: #eee; + +$light-passing-color: #a6b779; +$passing-color: #5e7d00; + +$light-failing-color: #cf867e; +$failing-color: #b03911; + +$neutral-color: #bababa; + +$font-size: 11px; +$large-font-size: 14px; + +body { + background-color: $page-background-color; + padding: 0; + margin: 5px; + overflow-y: scroll; +} + +#HTMLReporter { + + font-size: $font-size; + font-family: Monaco, "Lucida Console", monospace; + line-height: $line-height; + color: $text-color; + + a { + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } + + p, h1, h2, h3, h4, h5, h6 { + margin: 0; + line-height: $line-height; + } + + .banner, + .symbolSummary, + .summary, + .resultMessage, + .specDetail .description, + .alert .bar, + .stackTrace { + padding-left: $margin-unit - 5px; + padding-right: $margin-unit - 5px; + } + + // This div is available for testing elements that must be added to the DOM. + // We position it out of view, so it doesn't obstruct the runner. + #jasmine_content { + position: fixed; + right: 100%; + } + + .version { + color: $feint-text-color; + } + + //--- Banner ---// + + .banner { + margin-top: $line-height; + } + + .duration { + color: $feint-text-color; + float: right; + } + + //--- Symbol summary ---// + + .symbolSummary { + @include clearfix; + margin: $line-height 0; + + li { + display: block; + float: left; + height: $line-height / 2; + width: $line-height; + margin-bottom: $line-height / 2; + + //opacity: .9; + + font-size: 16px; + + &.passed { + font-size: 14px; + + &:before{ + color: $passing-color; + content: "\02022"; + } + } + + &.failed { + line-height: ($line-height / 2) + 2; + + &:before{ + color: $failing-color; + content: "x"; + font-weight: bold; + margin-left: -1px; + } + } + + &.skipped { + font-size: 14px; + + &:before{ + color: $neutral-color; + content: "\02022"; + } + } + + &.pending{ + line-height: ($line-height / 2) + 4; + + &:before { + color: $feint-text-color; + content: "-"; + } + } + } + } + + //--- Alert ---// + + .bar { + line-height: $line-height * 2; + font-size: $large-font-size; + + display: block; + color: #eee; + } + + .runningAlert { + background-color: $light-text-color; + } + + .skippedAlert { + background-color: $feint-text-color; + + &:first-child { + background-color: $text-color; + } + + &:hover { + text-decoration: none; + color: white; + text-decoration: underline; + } + } + + .passingAlert { + background-color: $light-passing-color; + + &:first-child { + background-color: $passing-color; + } + } + + .failingAlert { + background-color: $light-failing-color; + + &:first-child { + background-color: $failing-color + } + } + + //--- Results ---// + + .results { + margin-top: $line-height; + } + + //--- Results menu ---// + + #details { + display: none; + } + + .resultsMenu, + .resultsMenu a { + background-color: #fff; + color: $text-color; + } + + &.showDetails { + + .summaryMenuItem { + font-weight: normal; + text-decoration: inherit; + + &:hover { + text-decoration: underline; + } + } + + .detailsMenuItem { + font-weight: bold; + text-decoration: underline; + } + + .summary { + display: none; + } + + #details { + display: block; + } + } + + .summaryMenuItem { + font-weight: bold; + text-decoration: underline; + } + + //--- Results summary ---// + + .summary { + margin-top: $margin-unit; + + .suite .suite, .specSummary { + margin-left: $margin-unit; + } + + .specSummary { + &.passed a { + color: $passing-color; + } + &.failed a { + color: $failing-color; + } + } + } + + .description+.suite { + margin-top: 0; + } + + .suite { + margin-top: $margin-unit; + + a { + color: $text-color; + } + } + + //--- Results details ---// + + #details { + .specDetail { + margin-bottom: $line-height * 2; + + .description { + //line-height: $line-height * 2; + display: block; + + color: white; + background-color: $failing-color; + + //font-size: $large-font-size; + } + } + } + + .resultMessage { + padding-top: $line-height; + + color: $text-color; + } + + .resultMessage span.result { + display: block; + } + + .stackTrace { + margin: 5px 0 0 0; + max-height: $line-height * 16; + overflow: auto; + line-height: 18px; + + color: $light-text-color; + border: 1px solid #ddd; + background: white; + white-space: pre; + } +} \ No newline at end of file diff --git a/src/html/_TrivialReporter.scss b/src/html/_TrivialReporter.scss new file mode 100644 index 00000000..c4e0caa9 --- /dev/null +++ b/src/html/_TrivialReporter.scss @@ -0,0 +1,169 @@ +#TrivialReporter { + padding: 8px 13px; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: scroll; + + background-color: white; + font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; + + a:visited, a { + color: #303; + } + + a:hover, a:active { + color: blue; + } + + .run_spec { + float:right; + padding-right: 5px; + font-size: .8em; + text-decoration: none; + } + + .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 { + background-color: yellow; + } + + + .options { + text-align: right; + font-size: .8em; + } + + + + + .suite { + border: 1px outset gray; + margin: 5px 0; + padding-left: 1em; + } + + .suite .suite { + margin: 5px; + } + + .suite.passed { + background-color: #dfd; + } + + .suite.failed { + background-color: #fdd; + } + + .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: #fbb; + } + + .skipped { + color: #777; + background-color: #eee; + display: none; + } + + + /*.resultMessage {*/ + /*white-space: pre;*/ + /*}*/ + + .resultMessage span.result { + display: block; + line-height: 2em; + color: black; + } + + .resultMessage .mismatch { + color: black; + } + + .stackTrace { + white-space: pre; + font-size: .8em; + margin-left: 10px; + 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/src/html/jasmine.css b/src/html/jasmine.css index 6583fe7c..826e5753 100644 --- a/src/html/jasmine.css +++ b/src/html/jasmine.css @@ -1,166 +1,81 @@ -body { - font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; -} +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; } +#HTMLReporter a { text-decoration: none; } +#HTMLReporter a:hover { text-decoration: underline; } +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; } +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; } +#HTMLReporter #jasmine_content { position: fixed; right: 100%; } +#HTMLReporter .version { color: #aaaaaa; } +#HTMLReporter .banner { margin-top: 14px; } +#HTMLReporter .duration { color: #aaaaaa; float: right; } +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; } +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; } +#HTMLReporter .symbolSummary li.passed { font-size: 14px; } +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; } +#HTMLReporter .symbolSummary li.failed { line-height: 9px; } +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; } +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; } +#HTMLReporter .symbolSummary li.pending { line-height: 11px; } +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; } +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } +#HTMLReporter .runningAlert { background-color: #666666; } +#HTMLReporter .skippedAlert { background-color: #aaaaaa; } +#HTMLReporter .skippedAlert:first-child { background-color: #333333; } +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; } +#HTMLReporter .passingAlert { background-color: #a6b779; } +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; } +#HTMLReporter .failingAlert { background-color: #cf867e; } +#HTMLReporter .failingAlert:first-child { background-color: #b03911; } +#HTMLReporter .results { margin-top: 14px; } +#HTMLReporter #details { display: none; } +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; } +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; } +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; } +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter.showDetails .summary { display: none; } +#HTMLReporter.showDetails #details { display: block; } +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; } +#HTMLReporter .summary { margin-top: 14px; } +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; } +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; } +#HTMLReporter .summary .specSummary.failed a { color: #b03911; } +#HTMLReporter .description + .suite { margin-top: 0; } +#HTMLReporter .suite { margin-top: 14px; } +#HTMLReporter .suite a { color: #333333; } +#HTMLReporter #details .specDetail { margin-bottom: 28px; } +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; } +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; } +#HTMLReporter .resultMessage span.result { display: block; } +#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; } -.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 { - background-color: yellow; -} - - -.options { - text-align: right; - font-size: .8em; -} - - - - -.suite { - border: 1px outset gray; - margin: 5px 0; - padding-left: 1em; -} - -.suite .suite { - margin: 5px; -} - -.suite.passed { - background-color: #dfd; -} - -.suite.failed { - background-color: #fdd; -} - -.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: #fbb; -} - -.skipped { - color: #777; - background-color: #eee; - display: none; -} - - -/*.resultMessage {*/ - /*white-space: pre;*/ -/*}*/ - -.resultMessage span.result { - display: block; - line-height: 2em; - color: black; -} - -.resultMessage .mismatch { - color: black; -} - -.stackTrace { - white-space: pre; - font-size: .8em; - margin-left: 10px; - 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; -} +#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ } +#TrivialReporter a:visited, #TrivialReporter a { color: #303; } +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; } +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; } +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; } +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; } +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; } +#TrivialReporter .runner.running { background-color: yellow; } +#TrivialReporter .options { text-align: right; font-size: .8em; } +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; } +#TrivialReporter .suite .suite { margin: 5px; } +#TrivialReporter .suite.passed { background-color: #dfd; } +#TrivialReporter .suite.failed { background-color: #fdd; } +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; } +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; } +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; } +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; } +#TrivialReporter .spec.skipped { background-color: #bbb; } +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; } +#TrivialReporter .passed { background-color: #cfc; display: none; } +#TrivialReporter .failed { background-color: #fbb; } +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; } +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; } +#TrivialReporter .resultMessage .mismatch { color: black; } +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; } +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; } +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; } +#TrivialReporter #jasmine_content { position: fixed; right: 100%; } +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; } diff --git a/src/html/jasmine.scss b/src/html/jasmine.scss new file mode 100644 index 00000000..267b3caa --- /dev/null +++ b/src/html/jasmine.scss @@ -0,0 +1,2 @@ +@import "HTMLReporter"; +@import "TrivialReporter"; \ No newline at end of file diff --git a/src/version.js b/src/version.js index 1890df5d..34491afa 100644 --- a/src/version.js +++ b/src/version.js @@ -2,5 +2,5 @@ jasmine.version_= { "major": 1, "minor": 1, "build": 0, - "revision": 1315677058 + "revision": 1320442951 }; diff --git a/tasks/build_dist.rb b/tasks/build_dist.rb index 242fde24..6b6126a9 100644 --- a/tasks/build_dist.rb +++ b/tasks/build_dist.rb @@ -2,9 +2,12 @@ desc "Build core jasmine.js" task :build_dist => [:lint, :write_version_files] do puts 'Building Jasmine distribution from source'.cyan + puts 'Building JavaScript'.yellow concat_into('./lib/jasmine-core/jasmine.js') { core_sources + version_source_file } concat_into('./lib/jasmine-core/jasmine-html.js') { html_sources } + puts 'Buliding CSS'.yellow + system("compass compile") FileUtils.cp('./src/html/jasmine.css', './lib/jasmine-core/jasmine.css') end diff --git a/tasks/helpers.rb b/tasks/helpers.rb index 27dec3b5..66c9f683 100644 --- a/tasks/helpers.rb +++ b/tasks/helpers.rb @@ -9,7 +9,7 @@ def core_sources end def html_sources - Dir.glob('./src/html/*.js') + ['./src/html/HtmlReporterHelpers.js'] + Dir.glob('./src/html/*.js') end def console_sources