Skip to content
This repository
Browse code

Add new HTMLReporter

  • Loading branch information...
commit ad4f48dcd4ac6a80c9003183997e8e1dae45026c 1 parent de9e2ab
Davis W. Frank & Sean Durham authored
2  .gitignore
@@ -10,3 +10,5 @@ site/
10 10 tags
11 11 Gemfile.lock
12 12 pkg/*
  13 +.sass-cache/*
  14 +src/html/.sass-cache/*
29 config.rb
... ... @@ -0,0 +1,29 @@
  1 +#
  2 +# Compass configuration file - for building Jasmine's 'final CSS files
  3 +#
  4 +
  5 +# Require any additional compass plugins here.
  6 +
  7 +# Set this to the root of your project when deployed:
  8 +http_path = "/"
  9 +css_dir = "src/html"
  10 +sass_dir = "src/html"
  11 +images_dir = "images"
  12 +javascripts_dir = "javascripts"
  13 +
  14 +# You can select your preferred output style here (can be overridden via the command line):
  15 +# output_style = :expanded or :nested or :compact or :compressed
  16 +output_style = :compact
  17 +
  18 +# To enable relative paths to assets via compass helper functions. Uncomment:
  19 +# relative_assets = true
  20 +
  21 +# To disable debugging comments that display the original location of your selectors. Uncomment:
  22 +line_comments = false
  23 +
  24 +
  25 +# If you prefer the indented syntax, you might want to regenerate this
  26 +# project again passing --syntax sass, or you can uncomment this:
  27 +# preferred_syntax = :sass
  28 +# and then run:
  29 +# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
2  jasmine-core.gemspec
@@ -18,5 +18,7 @@ Gem::Specification.new do |s|
18 18 s.add_development_dependency "term-ansicolor"
19 19 s.add_development_dependency "json_pure", ">= 1.4.3"
20 20 s.add_development_dependency "frank"
  21 + s.add_development_dependency "sass"
  22 + s.add_development_dependency "compass"
21 23 s.add_development_dependency "ragaskar-jsdoc_helper"
22 24 end
488 lib/jasmine-core/jasmine-html.js
... ... @@ -1,3 +1,489 @@
  1 +jasmine.HtmlReporterHelpers = {};
  2 +
  3 +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
  4 + var el = document.createElement(type);
  5 +
  6 + for (var i = 2; i < arguments.length; i++) {
  7 + var child = arguments[i];
  8 +
  9 + if (typeof child === 'string') {
  10 + el.appendChild(document.createTextNode(child));
  11 + } else {
  12 + if (child) {
  13 + el.appendChild(child);
  14 + }
  15 + }
  16 + }
  17 +
  18 + for (var attr in attrs) {
  19 + if (attr == "className") {
  20 + el[attr] = attrs[attr];
  21 + } else {
  22 + el.setAttribute(attr, attrs[attr]);
  23 + }
  24 + }
  25 +
  26 + return el;
  27 +};
  28 +
  29 +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
  30 + var results = child.results();
  31 + var status = results.passed() ? 'passed' : 'failed';
  32 + if (results.skipped) {
  33 + status = 'skipped';
  34 + }
  35 +
  36 + return status;
  37 +};
  38 +
  39 +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
  40 + var parentDiv = this.dom.summary;
  41 + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
  42 + var parent = child[parentSuite];
  43 +
  44 + if (parent) {
  45 + if (typeof this.views.suites[parent.id] == 'undefined') {
  46 + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
  47 + }
  48 + parentDiv = this.views.suites[parent.id].element;
  49 + }
  50 +
  51 + parentDiv.appendChild(childElement);
  52 +};
  53 +
  54 +
  55 +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
  56 + for(var fn in jasmine.HtmlReporterHelpers) {
  57 + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
  58 + }
  59 +};
  60 +
  61 +jasmine.HtmlReporter = function(_doc) {
  62 + var self = this;
  63 + var doc = _doc || window.document;
  64 +
  65 + var reporterView;
  66 +
  67 + var dom = {};
  68 +
  69 + // Jasmine Reporter Public Interface
  70 + self.logRunningSpecs = false;
  71 +
  72 + self.reportRunnerStarting = function(runner) {
  73 + var specs = runner.specs() || [];
  74 +
  75 + if (specs.length == 0) {
  76 + return;
  77 + }
  78 +
  79 + createReporterDom(runner.env.versionString());
  80 + doc.body.appendChild(dom.reporter);
  81 +
  82 + reporterView = new jasmine.HtmlReporter.ReporterView(dom);
  83 + reporterView.addSpecs(specs, self.specFilter);
  84 + };
  85 +
  86 + self.reportRunnerResults = function(runner) {
  87 + reporterView.complete();
  88 + };
  89 +
  90 + self.reportSuiteResults = function(suite) {
  91 + reporterView.suiteComplete(suite);
  92 + };
  93 +
  94 + self.reportSpecStarting = function(spec) {
  95 + if (self.logRunningSpecs) {
  96 + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  97 + }
  98 + };
  99 +
  100 + self.reportSpecResults = function(spec) {
  101 + reporterView.specComplete(spec);
  102 + };
  103 +
  104 + self.log = function() {
  105 + var console = jasmine.getGlobal().console;
  106 + if (console && console.log) {
  107 + if (console.log.apply) {
  108 + console.log.apply(console, arguments);
  109 + } else {
  110 + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
  111 + }
  112 + }
  113 + };
  114 +
  115 + self.specFilter = function(spec) {
  116 + if (!focusedSpecName()) {
  117 + return true;
  118 + }
  119 +
  120 + return spec.getFullName().indexOf(focusedSpecName()) === 0;
  121 + };
  122 +
  123 + return self;
  124 +
  125 + function focusedSpecName() {
  126 + var specName;
  127 +
  128 + (function memoizeFocusedSpec() {
  129 + if (specName) {
  130 + return;
  131 + }
  132 +
  133 + var paramMap = [];
  134 + var params = doc.location.search.substring(1).split('&');
  135 +
  136 + for (var i = 0; i < params.length; i++) {
  137 + var p = params[i].split('=');
  138 + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  139 + }
  140 +
  141 + specName = paramMap.spec;
  142 + })();
  143 +
  144 + return specName;
  145 + }
  146 +
  147 + function createReporterDom(version) {
  148 + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
  149 + dom.banner = self.createDom('div', { className: 'banner' },
  150 + self.createDom('span', { className: 'title' }, "Jasmine "),
  151 + self.createDom('span', { className: 'version' }, version)),
  152 +
  153 + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
  154 + dom.alert = self.createDom('div', {className: 'alert'}),
  155 + dom.results = self.createDom('div', {className: 'results'},
  156 + dom.summary = self.createDom('div', { className: 'summary' }),
  157 + dom.details = self.createDom('div', { id: 'details' }))
  158 + );
  159 + }
  160 +};
  161 +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {};
  162 +
  163 +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
  164 + var el = document.createElement(type);
  165 +
  166 + for (var i = 2; i < arguments.length; i++) {
  167 + var child = arguments[i];
  168 +
  169 + if (typeof child === 'string') {
  170 + el.appendChild(document.createTextNode(child));
  171 + } else {
  172 + if (child) {
  173 + el.appendChild(child);
  174 + }
  175 + }
  176 + }
  177 +
  178 + for (var attr in attrs) {
  179 + if (attr == "className") {
  180 + el[attr] = attrs[attr];
  181 + } else {
  182 + el.setAttribute(attr, attrs[attr]);
  183 + }
  184 + }
  185 +
  186 + return el;
  187 +};
  188 +
  189 +jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
  190 + var results = child.results();
  191 + var status = results.passed() ? 'passed' : 'failed';
  192 + if (results.skipped) {
  193 + status = 'skipped';
  194 + }
  195 +
  196 + return status;
  197 +};
  198 +
  199 +jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
  200 + var parentDiv = this.dom.summary;
  201 + var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
  202 + var parent = child[parentSuite];
  203 +
  204 + if (parent) {
  205 + if (typeof this.views.suites[parent.id] == 'undefined') {
  206 + this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
  207 + }
  208 + parentDiv = this.views.suites[parent.id].element;
  209 + }
  210 +
  211 + parentDiv.appendChild(childElement);
  212 +};
  213 +
  214 +
  215 +jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
  216 + for(var fn in jasmine.HtmlReporterHelpers) {
  217 + ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
  218 + }
  219 +};
  220 +
  221 +jasmine.HtmlReporter.ReporterView = function(dom) {
  222 + this.startedAt = new Date();
  223 + this.runningSpecCount = 0;
  224 + this.completeSpecCount = 0;
  225 + this.passedCount = 0;
  226 + this.failedCount = 0;
  227 + this.skippedCount = 0;
  228 +
  229 + this.createResultsMenu = function() {
  230 + this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
  231 + this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
  232 + ' | ',
  233 + this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
  234 +
  235 + this.summaryMenuItem.onclick = function() {
  236 + dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
  237 + };
  238 +
  239 + this.detailsMenuItem.onclick = function() {
  240 + showDetails();
  241 + };
  242 + };
  243 +
  244 + this.addSpecs = function(specs, specFilter) {
  245 + this.totalSpecCount = specs.length;
  246 +
  247 + this.views = {
  248 + specs: {},
  249 + suites: {}
  250 + };
  251 +
  252 + for (var i = 0; i < specs.length; i++) {
  253 + var spec = specs[i];
  254 + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
  255 + if (specFilter(spec)) {
  256 + this.runningSpecCount++;
  257 + }
  258 + }
  259 + };
  260 +
  261 + this.specComplete = function(spec) {
  262 + this.completeSpecCount++;
  263 +
  264 + if (isUndefined(this.views.specs[spec.id])) {
  265 + this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
  266 + }
  267 +
  268 + var specView = this.views.specs[spec.id];
  269 +
  270 + switch (specView.status()) {
  271 + case 'passed':
  272 + this.passedCount++;
  273 + break;
  274 +
  275 + case 'failed':
  276 + this.failedCount++;
  277 + break;
  278 +
  279 + case 'skipped':
  280 + this.skippedCount++;
  281 + break;
  282 + }
  283 +
  284 + specView.refresh();
  285 + this.refresh();
  286 + };
  287 +
  288 + this.suiteComplete = function(suite) {
  289 + var suiteView = this.views.suites[suite.id];
  290 + if (isUndefined(suiteView)) {
  291 + return;
  292 + }
  293 + suiteView.refresh();
  294 + };
  295 +
  296 + this.refresh = function() {
  297 +
  298 + if (isUndefined(this.resultsMenu)) {
  299 + this.createResultsMenu();
  300 + }
  301 +
  302 + // currently running UI
  303 + if (isUndefined(this.runningAlert)) {
  304 + this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
  305 + dom.alert.appendChild(this.runningAlert);
  306 + }
  307 + this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
  308 +
  309 + // skipped specs UI
  310 + if (isUndefined(this.skippedAlert)) {
  311 + this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
  312 + }
  313 +
  314 + this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
  315 +
  316 + if (this.skippedCount === 1 && isDefined(dom.alert)) {
  317 + dom.alert.appendChild(this.skippedAlert);
  318 + }
  319 +
  320 + // passing specs UI
  321 + if (isUndefined(this.passedAlert)) {
  322 + this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
  323 + }
  324 + this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
  325 +
  326 + // failing specs UI
  327 + if (isUndefined(this.failedAlert)) {
  328 + this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
  329 + }
  330 + this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
  331 +
  332 + if (this.failedCount === 1 && isDefined(dom.alert)) {
  333 + dom.alert.appendChild(this.failedAlert);
  334 + dom.alert.appendChild(this.resultsMenu);
  335 + }
  336 +
  337 + // summary info
  338 + this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
  339 + this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
  340 + };
  341 +
  342 + this.complete = function() {
  343 + dom.alert.removeChild(this.runningAlert);
  344 +
  345 + this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
  346 +
  347 + if (this.failedCount === 0) {
  348 + dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
  349 + } else {
  350 + showDetails();
  351 + }
  352 +
  353 + dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
  354 + };
  355 +
  356 + return this;
  357 +
  358 + function showDetails() {
  359 + if (dom.reporter.className.search(/showDetails/) === -1) {
  360 + dom.reporter.className += " showDetails";
  361 + }
  362 + }
  363 +
  364 + function isUndefined(obj) {
  365 + return typeof obj === 'undefined';
  366 + }
  367 +
  368 + function isDefined(obj) {
  369 + return !isUndefined(obj);
  370 + }
  371 +
  372 + function specPluralizedFor(count) {
  373 + var str = count + " spec";
  374 + if (count > 1) {
  375 + str += "s"
  376 + }
  377 + return str;
  378 + }
  379 +
  380 +};
  381 +
  382 +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
  383 +
  384 +
  385 +jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
  386 + this.spec = spec;
  387 + this.dom = dom;
  388 + this.views = views;
  389 +
  390 + this.symbol = this.createDom('li', { className: 'pending' });
  391 + this.dom.symbolSummary.appendChild(this.symbol);
  392 +
  393 + this.summary = this.createDom('div', { className: 'specSummary' },
  394 + this.createDom('a', {
  395 + className: 'description',
  396 + href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
  397 + title: this.spec.getFullName()
  398 + }, this.spec.description)
  399 + );
  400 +
  401 + this.detail = this.createDom('div', { className: 'specDetail' },
  402 + this.createDom('a', {
  403 + className: 'description',
  404 + href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
  405 + title: this.spec.getFullName()
  406 + }, this.spec.getFullName())
  407 + );
  408 +};
  409 +
  410 +jasmine.HtmlReporter.SpecView.prototype.status = function() {
  411 + return this.getSpecStatus(this.spec);
  412 +};
  413 +
  414 +jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
  415 + this.symbol.className = this.status();
  416 +
  417 + switch (this.status()) {
  418 + case 'skipped':
  419 + break;
  420 +
  421 + case 'passed':
  422 + this.appendSummaryToSuiteDiv();
  423 + break;
  424 +
  425 + case 'failed':
  426 + this.appendSummaryToSuiteDiv();
  427 + this.appendFailureDetail();
  428 + break;
  429 + }
  430 +};
  431 +
  432 +jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
  433 + this.summary.className += ' ' + this.status();
  434 + this.appendToSummary(this.spec, this.summary);
  435 +};
  436 +
  437 +jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
  438 + this.detail.className += ' ' + this.status();
  439 +
  440 + var resultItems = this.spec.results().getItems();
  441 + var messagesDiv = this.createDom('div', { className: 'messages' });
  442 +
  443 + for (var i = 0; i < resultItems.length; i++) {
  444 + var result = resultItems[i];
  445 +
  446 + if (result.type == 'log') {
  447 + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
  448 + } else if (result.type == 'expect' && result.passed && !result.passed()) {
  449 + messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
  450 +
  451 + if (result.trace.stack) {
  452 + messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
  453 + }
  454 + }
  455 + }
  456 +
  457 + if (messagesDiv.childNodes.length > 0) {
  458 + this.detail.appendChild(messagesDiv);
  459 + this.dom.details.appendChild(this.detail);
  460 + }
  461 +};
  462 +
  463 +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
  464 + this.suite = suite;
  465 + this.dom = dom;
  466 + this.views = views;
  467 +
  468 + this.element = this.createDom('div', { className: 'suite' },
  469 + this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
  470 + );
  471 +
  472 + this.appendToSummary(this.suite, this.element);
  473 +};
  474 +
  475 +jasmine.HtmlReporter.SuiteView.prototype.status = function() {
  476 + return this.getSpecStatus(this.suite);
  477 +};
  478 +
  479 +jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
  480 + this.element.className += " " + this.status();
  481 +};
  482 +
  483 +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
  484 +
  485 +/* @deprecated Use jasmine.HtmlReporter instead
  486 + */
1 487 jasmine.TrivialReporter = function(doc) {
2 488 this.document = doc || document;
3 489 this.suiteDivs = {};
@@ -31,7 +517,7 @@ jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarA
31 517 jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
32 518 var showPassed, showSkipped;
33 519
34   - this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
  520 + this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
35 521 this.createDom('div', { className: 'banner' },
36 522 this.createDom('div', { className: 'logo' },
37 523 this.createDom('span', { className: 'title' }, "Jasmine"),
247 lib/jasmine-core/jasmine.css
... ... @@ -1,166 +1,81 @@
1   -body {
2   - font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
3   -}
4   -
5   -
6   -.jasmine_reporter a:visited, .jasmine_reporter a {
7   - color: #303;
8   -}
9   -
10   -.jasmine_reporter a:hover, .jasmine_reporter a:active {
11   - color: blue;
12   -}
13   -
14   -.run_spec {
15   - float:right;
16   - padding-right: 5px;
17   - font-size: .8em;
18   - text-decoration: none;
19   -}
20   -
21   -.jasmine_reporter {
22   - margin: 0 5px;
23   -}
24   -
25   -.banner {
26   - color: #303;
27   - background-color: #fef;
28   - padding: 5px;
29   -}
30   -
31   -.logo {
32   - float: left;
33   - font-size: 1.1em;
34   - padding-left: 5px;
35   -}
36   -
37   -.logo .version {
38   - font-size: .6em;
39   - padding-left: 1em;
40   -}
41   -
42   -.runner.running {
43   - background-color: yellow;
44   -}
45   -
46   -
47   -.options {
48   - text-align: right;
49   - font-size: .8em;
50   -}
51   -
52   -
53   -
54   -
55   -.suite {
56   - border: 1px outset gray;
57   - margin: 5px 0;
58   - padding-left: 1em;
59   -}
60   -
61   -.suite .suite {
62   - margin: 5px;
63   -}
64   -
65   -.suite.passed {
66   - background-color: #dfd;
67   -}
68   -
69   -.suite.failed {
70   - background-color: #fdd;
71   -}
72   -
73   -.spec {
74   - margin: 5px;
75   - padding-left: 1em;
76   - clear: both;
77   -}
78   -
79   -.spec.failed, .spec.passed, .spec.skipped {
80   - padding-bottom: 5px;
81   - border: 1px solid gray;
82   -}
83   -
84   -.spec.failed {
85   - background-color: #fbb;
86   - border-color: red;
87   -}
88   -
89   -.spec.passed {
90   - background-color: #bfb;
91   - border-color: green;
92   -}
93   -
94   -.spec.skipped {
95   - background-color: #bbb;
96   -}
97   -
98   -.messages {
99   - border-left: 1px dashed gray;
100   - padding-left: 1em;
101   - padding-right: 1em;
102   -}
103   -
104   -.passed {
105   - background-color: #cfc;
106   - display: none;
107   -}
108   -
109   -.failed {
110   - background-color: #fbb;
111   -}
112   -
113   -.skipped {
114   - color: #777;
115   - background-color: #eee;
116   - display: none;
117   -}
118   -
119   -
120   -/*.resultMessage {*/
121   - /*white-space: pre;*/
122   -/*}*/
123   -
124   -.resultMessage span.result {
125   - display: block;
126   - line-height: 2em;
127   - color: black;
128   -}
129   -
130   -.resultMessage .mismatch {
131   - color: black;
132   -}
133   -
134   -.stackTrace {
135   - white-space: pre;
136   - font-size: .8em;
137   - margin-left: 10px;
138   - max-height: 5em;
139   - overflow: auto;
140   - border: 1px inset red;
141   - padding: 1em;
142   - background: #eef;
143   -}
144   -
145   -.finished-at {
146   - padding-left: 1em;
147   - font-size: .6em;
148   -}
149   -
150   -.show-passed .passed,
151   -.show-skipped .skipped {
152   - display: block;
153   -}
154   -
155   -
156   -#jasmine_content {
157   - position:fixed;
158   - right: 100%;
159   -}
160   -
161   -.runner {
162   - border: 1px solid gray;
163   - display: block;
164   - margin: 5px 0;
165   - padding: 2px 0 2px 10px;
166   -}
  1 +body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
  2 +
  3 +#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
  4 +#HTMLReporter a { text-decoration: none; }
  5 +#HTMLReporter a:hover { text-decoration: underline; }
  6 +#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
  7 +#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
  8 +#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
  9 +#HTMLReporter .version { color: #aaaaaa; }
  10 +#HTMLReporter .banner { margin-top: 14px; }
  11 +#HTMLReporter .duration { color: #aaaaaa; float: right; }
  12 +#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
  13 +#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
  14 +#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
  15 +#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
  16 +#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
  17 +#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
  18 +#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
  19 +#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
  20 +#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
  21 +#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
  22 +#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
  23 +#HTMLReporter .runningAlert { background-color: #666666; }
  24 +#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
  25 +#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
  26 +#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
  27 +#HTMLReporter .passingAlert { background-color: #a6b779; }
  28 +#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
  29 +#HTMLReporter .failingAlert { background-color: #cf867e; }
  30 +#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
  31 +#HTMLReporter .results { margin-top: 14px; }
  32 +#HTMLReporter #details { display: none; }
  33 +#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
  34 +#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
  35 +#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
  36 +#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
  37 +#HTMLReporter.showDetails .summary { display: none; }
  38 +#HTMLReporter.showDetails #details { display: block; }
  39 +#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
  40 +#HTMLReporter .summary { margin-top: 14px; }
  41 +#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
  42 +#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
  43 +#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
  44 +#HTMLReporter .description + .suite { margin-top: 0; }
  45 +#HTMLReporter .suite { margin-top: 14px; }
  46 +#HTMLReporter .suite a { color: #333333; }
  47 +#HTMLReporter #details .specDetail { margin-bottom: 28px; }
  48 +#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
  49 +#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
  50 +#HTMLReporter .resultMessage span.result { display: block; }
  51 +#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; }
  52 +
  53 +#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;*/ /*}*/ }
  54 +#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
  55 +#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
  56 +#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
  57 +#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
  58 +#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
  59 +#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
  60 +#TrivialReporter .runner.running { background-color: yellow; }
  61 +#TrivialReporter .options { text-align: right; font-size: .8em; }
  62 +#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
  63 +#TrivialReporter .suite .suite { margin: 5px; }
  64 +#TrivialReporter .suite.passed { background-color: #dfd; }
  65 +#TrivialReporter .suite.failed { background-color: #fdd; }
  66 +#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
  67 +#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
  68 +#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
  69 +#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
  70 +#TrivialReporter .spec.skipped { background-color: #bbb; }
  71 +#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
  72 +#TrivialReporter .passed { background-color: #cfc; display: none; }
  73 +#TrivialReporter .failed { background-color: #fbb; }
  74 +#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
  75 +#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
  76 +#TrivialReporter .resultMessage .mismatch { color: black; }
  77 +#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
  78 +#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
  79 +#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
  80 +#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
  81 +#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
2  lib/jasmine-core/jasmine.js
@@ -2472,5 +2472,5 @@ jasmine.version_= {
2472 2472 "major": 1,
2473 2473 "minor": 1,
2474 2474 "build": 0,
2475   - "revision": 1315677058
  2475 + "revision": 1320442951
2476 2476 };
194 spec/html/HTMLReporterSpec.js
... ... @@ -0,0 +1,194 @@
  1 +describe("HtmlReporter", function() {
  2 + var env;
  3 + var htmlReporter;
  4 + var body;
  5 + var fakeDocument;
  6 +
  7 + beforeEach(function() {
  8 + env = new jasmine.Env();
  9 + env.updateInterval = 0;
  10 +
  11 + body = document.createElement("body");
  12 + fakeDocument = { body: body, location: { search: "" } };
  13 + htmlReporter = new jasmine.HtmlReporter(fakeDocument);
  14 + });
  15 +
  16 + function fakeSpec(name) {
  17 + return {
  18 + getFullName: function() {
  19 + return name;
  20 + }
  21 + };
  22 + }
  23 +
  24 + function findElements(divs, withClass) {
  25 + var els = [];
  26 + for (var i = 0; i < divs.length; i++) {
  27 + if (divs[i].className == withClass) els.push(divs[i]);
  28 + }
  29 + return els;
  30 + }
  31 +
  32 + function findElement(divs, withClass) {
  33 + var els = findElements(divs, withClass);
  34 + if (els.length > 0) {
  35 + return els[0];
  36 + }
  37 + throw new Error("couldn't find div with class " + withClass);
  38 + }
  39 +
  40 + it("should run only specs beginning with spec parameter", function() {
  41 + fakeDocument.location.search = "?spec=run%20this";
  42 + expect(htmlReporter.specFilter(fakeSpec("run this"))).toBeTruthy();
  43 + expect(htmlReporter.specFilter(fakeSpec("not the right spec"))).toBeFalsy();
  44 + expect(htmlReporter.specFilter(fakeSpec("not run this"))).toBeFalsy();
  45 + });
  46 +
  47 + describe('Matcher reporting', function () {
  48 + var getResultMessageDiv = function (body) {
  49 + var divs = body.getElementsByTagName("div");
  50 + for (var i = 0; i < divs.length; i++) {
  51 + if (divs[i].className.match(/resultMessage/)) {
  52 + return divs[i];
  53 + }
  54 + }
  55 + };
  56 +
  57 + var runner, spec, fakeTimer;
  58 + beforeEach(function () {
  59 + fakeTimer = new jasmine.FakeTimer();
  60 + env.setTimeout = fakeTimer.setTimeout;
  61 + env.clearTimeout = fakeTimer.clearTimeout;
  62 + env.setInterval = fakeTimer.setInterval;
  63 + env.clearInterval = fakeTimer.clearInterval;
  64 + runner = env.currentRunner();
  65 + var suite = new jasmine.Suite(env, 'some suite');
  66 + runner.add(suite);
  67 + spec = new jasmine.Spec(env, suite, 'some spec');
  68 + suite.add(spec);
  69 + fakeDocument.location.search = "?";
  70 + env.addReporter(htmlReporter);
  71 + });
  72 +
  73 + describe('toContain', function () {
  74 + it('should show actual and expected', function () {
  75 + spec.runs(function () {
  76 + this.expect('foo').toContain('bar');
  77 + });
  78 + runner.execute();
  79 + fakeTimer.tick(0);
  80 +
  81 + var resultEl = getResultMessageDiv(body);
  82 + expect(resultEl.innerHTML).toMatch(/foo/);
  83 + expect(resultEl.innerHTML).toMatch(/bar/);
  84 + });
  85 + });
  86 + });
  87 +
  88 + describe("failure messages (integration)", function () {
  89 + var spec, results, expectationResult;
  90 +
  91 + it("should add the failure message to the DOM (non-toEquals matchers)", function() {
  92 + env.describe("suite", function() {
  93 + env.it("will have log messages", function() {
  94 + this.expect('a').toBeNull();
  95 + });
  96 + });
  97 +
  98 + env.addReporter(htmlReporter);
  99 + env.execute();
  100 +
  101 + var divs = body.getElementsByTagName("div");
  102 + var errorDiv = findElement(divs, 'resultMessage fail');
  103 + expect(errorDiv.innerHTML).toMatch(/Expected 'a' to be null/);
  104 + });
  105 +
  106 + it("should add the failure message to the DOM (non-toEquals matchers) html escaping", function() {
  107 + env.describe("suite", function() {
  108 + env.it("will have log messages", function() {
  109 + this.expect('1 < 2').toBeNull();
  110 + });
  111 + });
  112 +
  113 + env.addReporter(htmlReporter);
  114 + env.execute();
  115 +
  116 + var divs = body.getElementsByTagName("div");
  117 + var errorDiv = findElement(divs, 'resultMessage fail');
  118 + expect(errorDiv.innerHTML).toMatch(/Expected '1 &lt; 2' to be null/);
  119 + });
  120 + });
  121 +
  122 + describe("log messages", function() {
  123 + it("should appear in the report of a failed spec", function() {
  124 + env.describe("suite", function() {
  125 + env.it("will have log messages", function() {
  126 + this.log("this is a", "multipart log message");
  127 + this.expect(true).toBeFalsy();
  128 + });
  129 + });
  130 +
  131 + env.addReporter(htmlReporter);
  132 + env.execute();
  133 +
  134 + var divs = body.getElementsByTagName("div");
  135 + var errorDiv = findElement(divs, 'specDetail failed');
  136 + expect(errorDiv.innerHTML).toMatch("this is a multipart log message");
  137 + });
  138 +
  139 + xit("should work on IE without console.log.apply", function() {
  140 + });
  141 + });
  142 +
  143 + describe("duplicate example names", function() {
  144 + it("should report failures correctly", function() {
  145 + var suite1 = env.describe("suite", function() {
  146 + env.it("will have log messages", function() {
  147 + this.log("this one fails!");
  148 + this.expect(true).toBeFalsy();
  149 + });
  150 + });
  151 +
  152 + var suite2 = env.describe("suite", function() {
  153 + env.it("will have log messages", function() {
  154 + this.log("this one passes!");
  155 + this.expect(true).toBeTruthy();
  156 + });
  157 + });
  158 +
  159 + env.addReporter(htmlReporter);
  160 + env.execute();
  161 +
  162 + var divs = body.getElementsByTagName("div");
  163 + var failedSpecDiv = findElement(divs, 'specDetail failed');
  164 + expect(failedSpecDiv.className).toEqual('specDetail failed');
  165 + expect(failedSpecDiv.innerHTML).toContain("this one fails!");
  166 + expect(failedSpecDiv.innerHTML).not.toContain("this one passes!");
  167 + });
  168 + });
  169 +
  170 + describe('#reportSpecStarting', function() {
  171 + beforeEach(function () {
  172 + env.describe("suite 1", function() {
  173 + env.it("spec 1", function() {
  174 + });
  175 + });
  176 + spyOn(htmlReporter, 'log').andCallThrough();
  177 + });
  178 +
  179 + it('DOES NOT log running specs by default', function() {
  180 + env.addReporter(htmlReporter);
  181 + env.execute();
  182 +
  183 + expect(htmlReporter.log).not.toHaveBeenCalled();
  184 + });
  185 +
  186 + it('logs running specs when log_running_specs is true', function() {
  187 + htmlReporter.logRunningSpecs = true;
  188 + env.addReporter(htmlReporter);
  189 + env.execute();
  190 +
  191 + expect(htmlReporter.log).toHaveBeenCalledWith('>> Jasmine Running suite 1 spec 1...');
  192 + });
  193 + });
  194 +});
13 spec/runner.html
@@ -15,6 +15,12 @@
15 15
16 16
17 17 <!-- include source files here... -->
  18 + <script type="text/javascript" src=".././src/html/HtmlReporterHelpers.js"></script>
  19 + <script type="text/javascript" src=".././src/html/HtmlReporter.js"></script>
  20 + <script type="text/javascript" src=".././src/html/HtmlReporterHelpers.js"></script>
  21 + <script type="text/javascript" src=".././src/html/ReporterView.js"></script>
  22 + <script type="text/javascript" src=".././src/html/SpecView.js"></script>
  23 + <script type="text/javascript" src=".././src/html/SuiteView.js"></script>
18 24 <script type="text/javascript" src=".././src/html/TrivialReporter.js"></script>
19 25 <script type="text/javascript" src=".././src/console/ConsoleReporter.js"></script>
20 26
@@ -38,6 +44,7 @@
38 44 <script type="text/javascript" src=".././spec/core/SuiteSpec.js"></script>
39 45 <script type="text/javascript" src=".././spec/core/UtilSpec.js"></script>
40 46 <script type="text/javascript" src=".././spec/core/WaitsForBlockSpec.js"></script>
  47 + <script type="text/javascript" src=".././spec/html/HTMLReporterSpec.js"></script>
41 48 <script type="text/javascript" src=".././spec/html/MatchersHtmlSpec.js"></script>
42 49 <script type="text/javascript" src=".././spec/html/PrettyPrintHtmlSpec.js"></script>
43 50 <script type="text/javascript" src=".././spec/html/TrivialReporterSpec.js"></script>
@@ -48,12 +55,12 @@
48 55 var jasmineEnv = jasmine.getEnv();
49 56 jasmineEnv.updateInterval = 1000;
50 57
51   - var trivialReporter = new jasmine.TrivialReporter();
  58 + var htmlReporter = new jasmine.HtmlReporter();
52 59
53   - jasmineEnv.addReporter(trivialReporter);
  60 + jasmineEnv.addReporter(htmlReporter);
54 61
55 62 jasmineEnv.specFilter = function(spec) {
56   - return trivialReporter.specFilter(spec);
  63 + return htmlReporter.specFilter(spec);
57 64 };
58 65
59 66 var currentWindowOnload = window.onload;
6 spec/templates/runner.html.erb
@@ -18,12 +18,12 @@
18 18 var jasmineEnv = jasmine.getEnv();
19 19 jasmineEnv.updateInterval = 1000;
20 20
21   - var trivialReporter = new jasmine.TrivialReporter();
  21 + var htmlReporter = new jasmine.HtmlReporter();
22 22
23   - jasmineEnv.addReporter(trivialReporter);
  23 + jasmineEnv.addReporter(htmlReporter);
24 24
25 25 jasmineEnv.specFilter = function(spec) {
26   - return trivialReporter.specFilter(spec);
  26 + return htmlReporter.specFilter(spec);
27 27 };
28 28
29 29 var currentWindowOnload = window.onload;
101 src/html/HtmlReporter.js
... ... @@ -0,0 +1,101 @@
  1 +jasmine.HtmlReporter = function(_doc) {
  2 + var self = this;
  3 + var doc = _doc || window.document;
  4 +
  5 + var reporterView;
  6 +
  7 + var dom = {};
  8 +
  9 + // Jasmine Reporter Public Interface
  10 + self.logRunningSpecs = false;
  11 +
  12 + self.reportRunnerStarting = function(runner) {
  13 + var specs = runner.specs() || [];
  14 +
  15 + if (specs.length == 0) {
  16 + return;
  17 + }
  18 +
  19 + createReporterDom(runner.env.versionString());
  20 + doc.body.appendChild(dom.reporter);
  21 +
  22 + reporterView = new jasmine.HtmlReporter.ReporterView(dom);
  23 + reporterView.addSpecs(specs, self.specFilter);
  24 + };
  25 +
  26 + self.reportRunnerResults = function(runner) {
  27 + reporterView.complete();
  28 + };
  29 +
  30 + self.reportSuiteResults = function(suite) {
  31 + reporterView.suiteComplete(suite);
  32 + };
  33 +
  34 + self.reportSpecStarting = function(spec) {
  35 + if (self.logRunningSpecs) {
  36 + self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  37 + }
  38 + };
  39 +
  40 + self.reportSpecResults = function(spec) {
  41 + reporterView.specComplete(spec);
  42 + };
  43 +
  44 + self.log = function() {
  45 + var console = jasmine.getGlobal().console;
  46 + if (console && console.log) {
  47 + if (console.log.apply) {
  48 + console.log.apply(console, arguments);
  49 + } else {
  50 + console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
  51 + }
  52 + }
  53 + };
  54 +
  55 + self.specFilter = function(spec) {
  56 + if (!focusedSpecName()) {
  57 + return true;
  58 + }
  59 +
  60 + return spec.getFullName().indexOf(focusedSpecName()) === 0;
  61 + };
  62 +
  63 + return self;
  64 +
  65 + function focusedSpecName() {
  66 + var specName;
  67 +
  68 + (function memoizeFocusedSpec() {
  69 + if (specName) {
  70 + return;
  71 + }
  72 +
  73 + var paramMap = [];
  74 + var params = doc.location.search.substring(1).split('&');
  75 +
  76 + for (var i = 0; i < params.length; i++) {
  77 + var p = params[i].split('=');
  78 + paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  79 + }
  80 +
  81 + specName = paramMap.spec;
  82 + })();
  83 +
  84 + return specName;
  85 + }
  86 +
  87 + function createReporterDom(version) {
  88 + dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
  89 + dom.banner = self.createDom('div', { className: 'banner' },
  90 + self.createDom('span', { className: 'title' }, "Jasmine "),
  91 + self.createDom('span', { className: 'version' }, version)),
  92 +
  93 + dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
  94 + dom.alert = self.createDom('div', {className: 'alert'}),
  95 + dom.results = self.createDom('div', {className: 'results'},
  96 + dom.summary = self.createDom('div', { className: 'summary' }),
  97 + dom.details = self.createDom('div', { id: 'details' }))
  98 + );
  99 + }
  100 +};
  101 +jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);
60 src/html/HtmlReporterHelpers.js
... ... @@ -0,0 +1,60 @@
  1 +jasmine.HtmlReporterHelpers = {};
  2 +
  3 +jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
  4 + var el = document.createElement(type);
  5 +
  6 + for (var i = 2; i < arguments.length; i++) {
  7 + var child = arguments[i];
  8 +
  9 + if (typeof child === 'string') {
  10 + el.appendChild(document.createTextNode(child));
  11 + } else {
  12 + if (child) {
  13 + el.appendChild(child);
  14 + }
  15 + }