From b7af6abca5d03535eabd0c25ab1c2f20380c80c8 Mon Sep 17 00:00:00 2001 From: "Dan Hansen and Davis W. Frank" Date: Thu, 28 Feb 2013 12:04:29 -0800 Subject: [PATCH] Support pending specs with: - xit - it with a null function body ( it("should be pending"); - calling pending() inside a spec - having a spec without any expectations Pending and Filtered specs now call Reporter interface specStarted so that reporting acts as expected. Pending and Filtered spec names are present and styled in the HTML reporter Using xit used to disable a spec. Disabling is now just when a spec is filtered out at run time (usually w/ the reporter). Suites are still disabled with xdescribe and means its specs are never executed. --- Gemfile | 2 +- lib/jasmine-core/boot/boot.js | 4 ++ lib/jasmine-core/jasmine-html.js | 11 ++- lib/jasmine-core/jasmine.css | 5 +- lib/jasmine-core/jasmine.js | 57 +++++++++++---- spec/console/ConsoleReporterSpec.js | 22 ++++-- spec/core/EnvSpec.js | 25 ++++++- spec/core/JsApiReporterSpec.js | 24 +------ spec/core/QueueRunnerSpec.js | 2 +- spec/core/SpecRunningSpec.js | 17 +++-- spec/core/SpecSpec.js | 88 ++++++++++++++++++----- spec/html/HtmlReporterSpec.js | 104 ++++++++++++++++++++++------ src/console/ConsoleReporter.js | 15 +++- src/core/Env.js | 14 +++- src/core/JsApiReporter.js | 6 +- src/core/QueueRunner.js | 4 +- src/core/Spec.js | 32 +++++++-- src/html/HtmlReporter.js | 11 ++- src/html/_HTMLReporter.scss | 24 ++++--- src/html/jasmine.css | 5 +- 20 files changed, 346 insertions(+), 126 deletions(-) diff --git a/Gemfile b/Gemfile index 845c0c50..af228af2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source :rubygems gem "rake" gem "jasmine", :git => 'https://github.com/pivotal/jasmine-gem.git', :branch => '2_0' - +#gem "jasmine", path: "/Users/pivotal/workspace/jasmine-gem" unless ENV["TRAVIS"] group :debug do gem 'debugger' diff --git a/lib/jasmine-core/boot/boot.js b/lib/jasmine-core/boot/boot.js index 30b6194f..aa29f812 100644 --- a/lib/jasmine-core/boot/boot.js +++ b/lib/jasmine-core/boot/boot.js @@ -30,6 +30,10 @@ return env.expect(actual); }, + pending: function() { + return env.pending(); + }, + addMatchers: function(matchers) { return env.addMatchers(matchers); }, diff --git a/lib/jasmine-core/jasmine-html.js b/lib/jasmine-core/jasmine-html.js index 10961d36..c55c0c2c 100644 --- a/lib/jasmine-core/jasmine-html.js +++ b/lib/jasmine-core/jasmine-html.js @@ -8,6 +8,7 @@ jasmine.HtmlReporter = function(options) { startTime, specsExecuted = 0, failureCount = 0, + pendingSpecCount = 0, htmlReporterMain, symbols; @@ -85,6 +86,10 @@ jasmine.HtmlReporter = function(options) { failures.push(failure); } + + if(result.status == "pending") { + pendingSpecCount++; + } }; this.jasmineDone = function() { @@ -116,8 +121,10 @@ jasmine.HtmlReporter = function(options) { ) ); } - var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount), - statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); + var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); + if(pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } + + var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); var results = find(".results")[0]; diff --git a/lib/jasmine-core/jasmine.css b/lib/jasmine-core/jasmine.css index 603ac73b..3196e974 100644 --- a/lib/jasmine-core/jasmine.css +++ b/lib/jasmine-core/jasmine.css @@ -18,8 +18,8 @@ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } .html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } .html-reporter .symbol-summary li.disabled { font-size: 14px; } .html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } -.html-reporter .symbol-summary li.pending { line-height: 11px; } -.html-reporter .symbol-summary li.pending:before { color: #aaaaaa; content: "-"; } +.html-reporter .symbol-summary li.pending { line-height: 17px; } +.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } .html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } .html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } .html-reporter .bar.failed { background-color: #b03911; } @@ -43,6 +43,7 @@ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } .html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } .html-reporter .summary li.passed a { color: #5e7d00; } .html-reporter .summary li.failed a { color: #b03911; } +.html-reporter .summary li.pending a { color: #ba9d37; } .html-reporter .description + .suite { margin-top: 0; } .html-reporter .suite { margin-top: 14px; } .html-reporter .suite a { color: #333333; } diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index 7d6c19e9..682e6930 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -599,6 +599,10 @@ jasmine.buildExpectationResult = function(options) { return catchExceptions; }; + this.catchException = function(e){ + return jasmine.Spec.isPendingSpecException(e) || catchExceptions; + }; + var maximumSpecCallbackDepth = 100; var currentSpecCallbackDepth = 0; @@ -613,13 +617,12 @@ jasmine.buildExpectationResult = function(options) { } var queueRunnerFactory = function(options) { - options.catchingExceptions = self.catchingExceptions; + options.catchException = self.catchException; options.encourageGC = options.encourageGarbageCollection || encourageGarbageCollection; new jasmine.QueueRunner(options).run(options.fns, 0); }; - var totalSpecsDefined = 0; this.specFactory = function(description, fn, suite) { totalSpecsDefined++; @@ -824,7 +827,7 @@ jasmine.buildExpectationResult = function(options) { // TODO: move this to closure jasmine.Env.prototype.xit = function(description, fn) { var spec = this.it(description, fn); - spec.disable(); + spec.pend(); return spec; }; @@ -838,6 +841,11 @@ jasmine.buildExpectationResult = function(options) { this.currentSuite.afterEach(afterEachFunction); }; + // TODO: move this to closure + jasmine.Env.prototype.pending = function() { + throw new Error(jasmine.Spec.pendingSpecExceptionMessage); + }; + // TODO: Still needed? jasmine.Env.prototype.currentRunner = function() { return this.topSuite; @@ -1014,12 +1022,12 @@ jasmine.JsApiReporter = function(jasmine) { }; var specs = []; - this.specStarted = function(result) { + this.specStarted = function(result) { }; + + this.specDone = function(result) { specs.push(result); }; - this.specDone = function(result) { }; - this.specResults = function(index, length) { return specs.slice(index, index + length); }; @@ -1558,7 +1566,7 @@ jasmine.QueueRunner = function(attrs) { this.onComplete = attrs.onComplete || function() {}; this.encourageGC = attrs.encourageGC || function(fn) {fn()}; this.onException = attrs.onException || function() {}; - this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.catchException = attrs.catchException || function() { return true; }; }; jasmine.QueueRunner.prototype.execute = function() { @@ -1585,7 +1593,7 @@ jasmine.QueueRunner.prototype.run = function(fns, index) { fn(); } catch (e) { self.onException(e); - if (!self.catchingExceptions()) { + if (!self.catchException(e)) { //TODO: set a var when we catch an exception and //use a finally block to close the loop in a nice way.. throw e; @@ -1606,10 +1614,14 @@ jasmine.Spec = function(attrs) { this.onStart = attrs.onStart || function() {}; this.exceptionFormatter = attrs.exceptionFormatter || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; - this.expectationResultFactory = attrs.expectationResultFactory || function() {}; - this.queueRunner = attrs.queueRunner || { execute: function() {}}; + this.expectationResultFactory = attrs.expectationResultFactory || function() { }; + this.queueRunner = attrs.queueRunner || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + if (!this.fn) { + this.pend(); + } + this.result = { id: this.id, description: this.description, @@ -1634,7 +1646,9 @@ jasmine.Spec.prototype.expect = function(actual) { jasmine.Spec.prototype.execute = function(onComplete) { var self = this; - if (this.disabled) { + this.onStart(this); + + if (this.markedPending || this.disabled) { complete(); return; } @@ -1643,10 +1657,14 @@ jasmine.Spec.prototype.execute = function(onComplete) { afters = this.afterFns() || []; var allFns = befores.concat(this.fn).concat(afters); - this.onStart(this); this.queueRunner({ fns: allFns, onException: function(e) { + if (jasmine.Spec.isPendingSpecException(e)) { + self.pend(); + return; + } + self.addExpectationResult(false, { matcherName: "", passed: false, @@ -1672,13 +1690,17 @@ jasmine.Spec.prototype.disable = function() { this.disabled = true; }; +jasmine.Spec.prototype.pend = function() { + this.markedPending = true; +}; + jasmine.Spec.prototype.status = function() { if (this.disabled) { return 'disabled'; } - if (!this.encounteredExpectations) { - return null; + if (this.markedPending || !this.encounteredExpectations) { + return 'pending'; } if (this.result.failedExpectations.length > 0) { @@ -1691,7 +1713,12 @@ jasmine.Spec.prototype.status = function() { jasmine.Spec.prototype.getFullName = function() { return this.getSpecName(this); }; -jasmine.Suite = function(attrs) { + +jasmine.Spec.pendingSpecExceptionMessage = "=> marked Pending"; + +jasmine.Spec.isPendingSpecException = function(e) { + return e.message.indexOf(jasmine.Spec.pendingSpecExceptionMessage) === 0; +};jasmine.Suite = function(attrs) { this.env = attrs.env; this.id = attrs.id; this.parentSuite = attrs.parentSuite; diff --git a/spec/console/ConsoleReporterSpec.js b/spec/console/ConsoleReporterSpec.js index 07ca1427..73241981 100644 --- a/spec/console/ConsoleReporterSpec.js +++ b/spec/console/ConsoleReporterSpec.js @@ -58,6 +58,16 @@ describe("ConsoleReporter", function() { expect(out.getOutput()).toEqual("F"); }); + it("reports a pending spec as a '*'", function() { + var reporter = new jasmine.ConsoleReporter({ + print: out.print + }); + + reporter.specDone({status: "pending"}); + + expect(out.getOutput()).toEqual("*"); + }); + it("reports a summary when done (singluar spec and time)", function() { var fakeNow = jasmine.createSpy('fake Date.now'), reporter = new jasmine.ConsoleReporter({ @@ -75,6 +85,7 @@ describe("ConsoleReporter", function() { reporter.jasmineDone(); expect(out.getOutput()).toMatch(/1 spec, 0 failures/); + expect(out.getOutput()).not.toMatch(/0 pending specs/); expect(out.getOutput()).toMatch("Finished in 1 second\n"); }); @@ -88,6 +99,7 @@ describe("ConsoleReporter", function() { fakeNow.andReturn(500); reporter.jasmineStarted(); reporter.specDone({status: "passed"}); + reporter.specDone({status: "pending"}); reporter.specDone({ status: "failed", description: "with a failing spec", @@ -98,9 +110,7 @@ describe("ConsoleReporter", function() { message: "Expected true to be false.", expected: false, actual: true, - trace: { - stack: "foo\nbar\nbaz" - } + stack: "foo\nbar\nbaz" } ] }); @@ -110,7 +120,7 @@ describe("ConsoleReporter", function() { fakeNow.andReturn(600); reporter.jasmineDone(); - expect(out.getOutput()).toMatch(/2 specs, 1 failure/); + expect(out.getOutput()).toMatch(/3 specs, 1 failure, 1 pending spec/); expect(out.getOutput()).toMatch("Finished in 0.1 seconds\n"); }); @@ -131,9 +141,7 @@ describe("ConsoleReporter", function() { message: "Expected true to be false.", expected: false, actual: true, - trace: { - stack: "foo bar baz" - } + stack: "foo bar baz" } ] }); diff --git a/spec/core/EnvSpec.js b/spec/core/EnvSpec.js index b9611489..95110d90 100644 --- a/spec/core/EnvSpec.js +++ b/spec/core/EnvSpec.js @@ -163,6 +163,29 @@ describe("Env", function() { }); }); }); + + describe("#catchException", function() { + it("returns true if the exception is a pending spec exception", function() { + env.catchExceptions(false); + + expect(env.catchException(new Error(jasmine.Spec.pendingSpecExceptionMessage))).toBe(true); + }); + + it("returns false if the exception is not a pending spec exception and not catching exceptions", function() { + env.catchExceptions(false); + + expect(env.catchException(new Error("external error"))).toBe(false); + expect(env.catchException(new Error(jasmine.Spec.pendingSpecExceptionMessage))).toBe(true); + }); + }); + + describe("#pending", function() { + it("throws the Pending Spec exception", function() { + expect(function() { + env.pending(); + }).toThrow(jasmine.Spec.pendingSpecExceptionMessage); + }); + }); }); describe("Env (integration)", function() { @@ -294,7 +317,7 @@ describe("Env (integration)", function() { env.expect(true).toBe(true); }); env.describe("with a nested suite", function() { - env.xit("with a disabled spec", function() { + env.xit("with a pending spec", function() { env.expect(true).toBe(true); }); env.it("with a spec", function() { diff --git a/spec/core/JsApiReporterSpec.js b/spec/core/JsApiReporterSpec.js index baa77f21..f89e5bb0 100644 --- a/spec/core/JsApiReporterSpec.js +++ b/spec/core/JsApiReporterSpec.js @@ -149,26 +149,6 @@ describe("JsApiReporter", function() { expect(suites).toEqual({123: {id: 123, description: "A suite", status: 'passed'}}); }); - it("tracks a spec", function() { - var reporter = new jasmine.JsApiReporter(), - result = { - id: 123, - description: "A spec" - }; - - reporter.specStarted(result); - - var specs = reporter.specs(); - - expect(specs).toEqual([result]); - - result.status = "passed"; - - reporter.specDone(result); - - expect(specs).toEqual([result]); - }); - describe("#specResults", function() { var reporter, specResult1, specResult2; beforeEach(function() { @@ -182,8 +162,8 @@ describe("JsApiReporter", function() { description: "Another spec" }; - reporter.specStarted(specResult1); - reporter.specStarted(specResult2); + reporter.specDone(specResult1); + reporter.specDone(specResult2); }); it("should return a slice of results", function() { diff --git a/spec/core/QueueRunnerSpec.js b/spec/core/QueueRunnerSpec.js index 933f3e67..3461e185 100644 --- a/spec/core/QueueRunnerSpec.js +++ b/spec/core/QueueRunnerSpec.js @@ -96,7 +96,7 @@ describe("QueueRunner", function() { }, queueRunner = new jasmine.QueueRunner({ fns: [fn], - catchingExceptions: function() { return false; } + catchException: function(e) { return false; } }); expect(function() { queueRunner.execute(); }).toThrow(); diff --git a/spec/core/SpecRunningSpec.js b/spec/core/SpecRunningSpec.js index 32e69f7c..d19950e2 100644 --- a/spec/core/SpecRunningSpec.js +++ b/spec/core/SpecRunningSpec.js @@ -224,7 +224,7 @@ describe("jasmine spec running", function () { var specInADisabledSuite = originalJasmine.createSpy("specInADisabledSuite"), suite = env.describe('A Suite', function() { env.xdescribe('with a disabled suite', function(){ - env.it('disabled spec', specInADisabledSuite); + env.it('spec inside a disabled suite', specInADisabledSuite); }); }); @@ -233,15 +233,18 @@ describe("jasmine spec running", function () { expect(specInADisabledSuite).not.toHaveBeenCalled(); }); - it("shouldn't run disabled tests", function() { - var disabledSpec = originalJasmine.createSpy('disabledSpec'), - suite = env.describe('default current suite', function() { - env.xit('disabled spec', disabledSpec); - }); + it("should set all pending specs to pending when a suite is run", function() { + var pendingSpec, + suite = env.describe('default current suite', function() { + pendingSpec = env.it("I am a pending spec"); + }); + suite.execute(); - expect(disabledSpec).not.toHaveBeenCalled(); + + expect(pendingSpec.status()).toBe("pending"); }); + // TODO: is this useful? It doesn't catch syntax errors xit("should recover gracefully when there are errors in describe functions", function() { var specs = []; diff --git a/spec/core/SpecSpec.js b/spec/core/SpecSpec.js index 949b81cb..422d97b9 100644 --- a/spec/core/SpecSpec.js +++ b/spec/core/SpecSpec.js @@ -1,5 +1,17 @@ describe("Spec", function() { + it("#isPendingSpecException returns true for a pending spec exception", function() { + var e = new Error(jasmine.Spec.pendingSpecExceptionMessage); + + expect(jasmine.Spec.isPendingSpecException(e)).toBe(true); + }); + + it("#isPendingSpecException returns true for a pending spec exception", function() { + var e = new Error("foo"); + + expect(jasmine.Spec.isPendingSpecException(e)).toBe(false); + }); + it("delegates execution to a QueueRunner", function() { var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), spec = new jasmine.Spec({ @@ -78,9 +90,24 @@ describe("Spec", function() { expect(allSpecFns).toEqual([before, fn, after]); }); - it("can be disabled, but still calls callbacks", function() { + it("is marked pending if created without a function body", function() { var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), + startCallback = originalJasmine.createSpy('startCallback'), + resultCallback = originalJasmine.createSpy('resultCallback'), + spec = new jasmine.Spec({ + onStart: startCallback, + fn: null, + resultCallback: resultCallback, + queueRunner: fakeQueueRunner + }); + + + expect(spec.status()).toBe('pending'); + }); + + it("can be disabled, but still calls callbacks", function() { + var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), startCallback = originalJasmine.createSpy('startCallback'), specBody = originalJasmine.createSpy('specBody'), resultCallback = originalJasmine.createSpy('resultCallback'), @@ -97,41 +124,39 @@ describe("Spec", function() { spec.execute(); - expect(startCallback).not.toHaveBeenCalled(); expect(fakeQueueRunner).not.toHaveBeenCalled(); expect(specBody).not.toHaveBeenCalled(); + expect(startCallback).toHaveBeenCalled(); expect(resultCallback).toHaveBeenCalled(); }); - it("should call the results callback on execution complete", function() { + it("can be marked pending, but still calls callbacks when executed", function() { var fakeQueueRunner = jasmine.createSpy('fakeQueueRunner'), - startCallback = originalJasmine.createSpy('startCallback'), - specBody = originalJasmine.createSpy('specBody'), resultCallback = originalJasmine.createSpy('resultCallback'), spec = new jasmine.Spec({ - onStart:startCallback, - fn: specBody, + onStart: startCallback, resultCallback: resultCallback, description: "with a spec", - getSpecName: function() { return "a suite with a spec"}, + getSpecName: function() { + return "a suite with a spec" + }, queueRunner: fakeQueueRunner }); - spec.disable(); + spec.pend(); - expect(spec.status()).toBe('disabled'); + expect(spec.status()).toBe('pending'); spec.execute(); - expect(startCallback).not.toHaveBeenCalled(); expect(fakeQueueRunner).not.toHaveBeenCalled(); - expect(specBody).not.toHaveBeenCalled(); + expect(startCallback).toHaveBeenCalled(); expect(resultCallback).toHaveBeenCalledWith({ id: spec.id, - status: 'disabled', + status: 'pending', description: 'with a spec', fullName: 'a suite with a spec', failedExpectations: [] @@ -152,19 +177,28 @@ describe("Spec", function() { expect(done).toHaveBeenCalled(); }); - it("#status returns null by default", function() { - var spec = new jasmine.Spec({}); - expect(spec.status()).toBeNull(); + it("#status returns pending by default", function() { + var spec = new jasmine.Spec({fn: jasmine.createSpy("spec body")}); + expect(spec.status()).toEqual('pending'); + }); + + it("#status returns pending if no expectations were encountered", function() { + var specBody = jasmine.createSpy("spec body"), + spec = new jasmine.Spec({fn: specBody}); + + spec.execute(); + + expect(spec.status()).toEqual('pending'); }); it("#status returns passed if all expectations in the spec have passed", function() { - var spec = new jasmine.Spec({}); + var spec = new jasmine.Spec({fn: jasmine.createSpy("spec body")}); spec.addExpectationResult(true); expect(spec.status()).toBe('passed'); }); it("#status returns failed if any expectations in the spec have failed", function() { - var spec = new jasmine.Spec({}); + var spec = new jasmine.Spec({ fn: jasmine.createSpy("spec body") }); spec.addExpectationResult(true); spec.addExpectationResult(false); expect(spec.status()).toBe('failed'); @@ -181,4 +215,22 @@ describe("Spec", function() { expect(spec.getFullName()).toBe('expected val'); }); + + describe("when a spec is marked pending during execution", function() { + it("should mark the spec as pending", function() { + var fakeQueueRunner = function(opts) { + opts.onException(new Error(jasmine.Spec.pendingSpecExceptionMessage)); + }, + spec = new jasmine.Spec({ + description: 'my test', + id: 'some-id', + fn: function() { }, + queueRunner: fakeQueueRunner + }); + + spec.execute(); + + expect(spec.status()).toEqual("pending"); + }); + }); }); diff --git a/spec/html/HtmlReporterSpec.js b/spec/html/HtmlReporterSpec.js index c87f5286..63631988 100644 --- a/spec/html/HtmlReporterSpec.js +++ b/spec/html/HtmlReporterSpec.js @@ -37,8 +37,7 @@ describe("New HtmlReporter", function() { var env = new jasmine.Env(), container = document.createElement("div"), getContainer = function() { return container; }, - getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ + reporter = new jasmine.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, @@ -54,12 +53,31 @@ describe("New HtmlReporter", function() { expect(specEl.getAttribute("id")).toEqual("spec_789"); }); + it("reports the status symbol of a pending spec", function() { + var env = new jasmine.Env(), + container = document.createElement("div"), + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.specDone({id: 789, status: "pending"}); + + var statuses = container.getElementsByClassName('symbol-summary')[0]; + var specEl = statuses.getElementsByTagName('li')[0]; + expect(specEl.getAttribute("class")).toEqual("pending"); + expect(specEl.getAttribute("id")).toEqual("spec_789"); + }); + it("reports the status symbol of a passing spec", function() { var env = new jasmine.Env(), container = document.createElement("div"), getContainer = function() { return container; }, - getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ + reporter = new jasmine.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, @@ -79,8 +97,7 @@ describe("New HtmlReporter", function() { var env = new jasmine.Env(), container = document.createElement("div"), getContainer = function() { return container; }, - getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ + reporter = new jasmine.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, @@ -108,7 +125,7 @@ describe("New HtmlReporter", function() { fakeNow = jasmine.createSpy('fake Date.now'), container = document.createElement("div"), getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ + reporter = new jasmine.HtmlReporter({ env: env, getContainer: getContainer, now: fakeNow, @@ -132,7 +149,7 @@ describe("New HtmlReporter", function() { var env = new jasmine.Env(), container = document.createElement("div"), getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ + reporter = new jasmine.HtmlReporter({ env: env, getContainer: getContainer, createElement: function() { return document.createElement.apply(document, arguments); }, @@ -300,12 +317,12 @@ describe("New HtmlReporter", function() { env = new jasmine.Env(); container = document.createElement("div"); getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ - env: env, - getContainer: getContainer, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); } - }); + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); reporter.initialize(); reporter.jasmineStarted({}); @@ -338,6 +355,51 @@ describe("New HtmlReporter", function() { expect(specFailure.childNodes.length).toEqual(0); }); + + it("reports no pending specs", function() { + var alert = container.getElementsByClassName("alert")[0]; + var alertBars = alert.getElementsByClassName("bar"); + + expect(alertBars[0].innerHTML).not.toMatch(/pending spec[s]/); + }); + }); + + describe("and there are pending specs", function() { + var env, container, reporter; + beforeEach(function() { + env = new jasmine.Env(); + container = document.createElement("div"); + getContainer = function() { return container; }, + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); + reporter.initialize(); + + reporter.jasmineStarted({}); + reporter.specDone({ + id: 123, + description: "with a spec", + fullName: "A Suite with a spec", + status: "pending" + }); + reporter.jasmineDone(); + }); + + it("reports the pending specs count", function() { + var alert = container.getElementsByClassName("alert")[0]; + var alertBars = alert.getElementsByClassName("bar"); + + expect(alertBars[0].innerHTML).toMatch(/1 spec, 0 failures, 1 pending spec/); + }); + + it("reports no failure details", function() { + var specFailure = container.getElementsByClassName("failures")[0]; + + expect(specFailure.childNodes.length).toEqual(0); + }); }); describe("and some tests fail", function() { @@ -345,14 +407,14 @@ describe("New HtmlReporter", function() { beforeEach(function() { env = new jasmine.Env(); - container = document.createElement("div"); + container = document.createElement("div"), getContainer = function() { return container; }, - reporter = new jasmine.HtmlReporter({ - env: env, - getContainer: getContainer, - createElement: function() { return document.createElement.apply(document, arguments); }, - createTextNode: function() { return document.createTextNode.apply(document, arguments); } - }); + reporter = new jasmine.HtmlReporter({ + env: env, + getContainer: getContainer, + createElement: function() { return document.createElement.apply(document, arguments); }, + createTextNode: function() { return document.createTextNode.apply(document, arguments); } + }); reporter.initialize(); reporter.jasmineStarted({}); diff --git a/src/console/ConsoleReporter.js b/src/console/ConsoleReporter.js index a41e32d1..52303cb1 100644 --- a/src/console/ConsoleReporter.js +++ b/src/console/ConsoleReporter.js @@ -7,6 +7,7 @@ jasmine.ConsoleReporter = function(options) { specCount, failureCount, failedSpecs = [], + pendingCount, ansi = { green: '\033[32m', red: '\033[31m', @@ -18,6 +19,7 @@ jasmine.ConsoleReporter = function(options) { startTime = now(); specCount = 0; failureCount = 0; + pendingCount = 0; print("Started"); printNewline(); }; @@ -33,6 +35,11 @@ jasmine.ConsoleReporter = function(options) { printNewline(); var specCounts = specCount + " " + plural("spec", specCount) + ", " + failureCount + " " + plural("failure", failureCount); + + if (pendingCount) { + specCounts += ", " + pendingCount + " pending " + plural("spec", pendingCount); + } + print(specCounts); printNewline(); @@ -47,6 +54,12 @@ jasmine.ConsoleReporter = function(options) { this.specDone = function(result) { specCount++; + if(result.status == "pending") { + pendingCount++; + print(colored("yellow", "*")); + return; + } + if (result.status == "passed") { print(colored("green", '.')); return; @@ -97,7 +110,7 @@ jasmine.ConsoleReporter = function(options) { for (var i = 0; i < result.failedExpectations.length; i++) { var failedExpectation = result.failedExpectations[i]; printNewline(); - print(indent(failedExpectation.trace.stack, 2)); + print(indent(failedExpectation.stack, 2)); } printNewline(); diff --git a/src/core/Env.js b/src/core/Env.js index ff743ca3..5dc71f1c 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -97,6 +97,10 @@ return catchExceptions; }; + this.catchException = function(e){ + return jasmine.Spec.isPendingSpecException(e) || catchExceptions; + }; + var maximumSpecCallbackDepth = 100; var currentSpecCallbackDepth = 0; @@ -111,13 +115,12 @@ } var queueRunnerFactory = function(options) { - options.catchingExceptions = self.catchingExceptions; + options.catchException = self.catchException; options.encourageGC = options.encourageGarbageCollection || encourageGarbageCollection; new jasmine.QueueRunner(options).run(options.fns, 0); }; - var totalSpecsDefined = 0; this.specFactory = function(description, fn, suite) { totalSpecsDefined++; @@ -322,7 +325,7 @@ // TODO: move this to closure jasmine.Env.prototype.xit = function(description, fn) { var spec = this.it(description, fn); - spec.disable(); + spec.pend(); return spec; }; @@ -336,6 +339,11 @@ this.currentSuite.afterEach(afterEachFunction); }; + // TODO: move this to closure + jasmine.Env.prototype.pending = function() { + throw new Error(jasmine.Spec.pendingSpecExceptionMessage); + }; + // TODO: Still needed? jasmine.Env.prototype.currentRunner = function() { return this.topSuite; diff --git a/src/core/JsApiReporter.js b/src/core/JsApiReporter.js index 02a4e57d..6819f178 100644 --- a/src/core/JsApiReporter.js +++ b/src/core/JsApiReporter.js @@ -38,12 +38,12 @@ jasmine.JsApiReporter = function(jasmine) { }; var specs = []; - this.specStarted = function(result) { + this.specStarted = function(result) { }; + + this.specDone = function(result) { specs.push(result); }; - this.specDone = function(result) { }; - this.specResults = function(index, length) { return specs.slice(index, index + length); }; diff --git a/src/core/QueueRunner.js b/src/core/QueueRunner.js index e6db4fd2..6a4fed4f 100644 --- a/src/core/QueueRunner.js +++ b/src/core/QueueRunner.js @@ -3,7 +3,7 @@ jasmine.QueueRunner = function(attrs) { this.onComplete = attrs.onComplete || function() {}; this.encourageGC = attrs.encourageGC || function(fn) {fn()}; this.onException = attrs.onException || function() {}; - this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + this.catchException = attrs.catchException || function() { return true; }; }; jasmine.QueueRunner.prototype.execute = function() { @@ -30,7 +30,7 @@ jasmine.QueueRunner.prototype.run = function(fns, index) { fn(); } catch (e) { self.onException(e); - if (!self.catchingExceptions()) { + if (!self.catchException(e)) { //TODO: set a var when we catch an exception and //use a finally block to close the loop in a nice way.. throw e; diff --git a/src/core/Spec.js b/src/core/Spec.js index 5b6db399..e90b4cf5 100644 --- a/src/core/Spec.js +++ b/src/core/Spec.js @@ -11,10 +11,14 @@ jasmine.Spec = function(attrs) { this.onStart = attrs.onStart || function() {}; this.exceptionFormatter = attrs.exceptionFormatter || function() {}; this.getSpecName = attrs.getSpecName || function() { return ''; }; - this.expectationResultFactory = attrs.expectationResultFactory || function() {}; - this.queueRunner = attrs.queueRunner || { execute: function() {}}; + this.expectationResultFactory = attrs.expectationResultFactory || function() { }; + this.queueRunner = attrs.queueRunner || function() {}; this.catchingExceptions = attrs.catchingExceptions || function() { return true; }; + if (!this.fn) { + this.pend(); + } + this.result = { id: this.id, description: this.description, @@ -39,7 +43,9 @@ jasmine.Spec.prototype.expect = function(actual) { jasmine.Spec.prototype.execute = function(onComplete) { var self = this; - if (this.disabled) { + this.onStart(this); + + if (this.markedPending || this.disabled) { complete(); return; } @@ -48,10 +54,14 @@ jasmine.Spec.prototype.execute = function(onComplete) { afters = this.afterFns() || []; var allFns = befores.concat(this.fn).concat(afters); - this.onStart(this); this.queueRunner({ fns: allFns, onException: function(e) { + if (jasmine.Spec.isPendingSpecException(e)) { + self.pend(); + return; + } + self.addExpectationResult(false, { matcherName: "", passed: false, @@ -77,13 +87,17 @@ jasmine.Spec.prototype.disable = function() { this.disabled = true; }; +jasmine.Spec.prototype.pend = function() { + this.markedPending = true; +}; + jasmine.Spec.prototype.status = function() { if (this.disabled) { return 'disabled'; } - if (!this.encounteredExpectations) { - return null; + if (this.markedPending || !this.encounteredExpectations) { + return 'pending'; } if (this.result.failedExpectations.length > 0) { @@ -96,3 +110,9 @@ jasmine.Spec.prototype.status = function() { jasmine.Spec.prototype.getFullName = function() { return this.getSpecName(this); }; + +jasmine.Spec.pendingSpecExceptionMessage = "=> marked Pending"; + +jasmine.Spec.isPendingSpecException = function(e) { + return e.message.indexOf(jasmine.Spec.pendingSpecExceptionMessage) === 0; +}; \ No newline at end of file diff --git a/src/html/HtmlReporter.js b/src/html/HtmlReporter.js index 09bcb8d3..e3697b85 100644 --- a/src/html/HtmlReporter.js +++ b/src/html/HtmlReporter.js @@ -8,6 +8,7 @@ jasmine.HtmlReporter = function(options) { startTime, specsExecuted = 0, failureCount = 0, + pendingSpecCount = 0, htmlReporterMain, symbols; @@ -85,6 +86,10 @@ jasmine.HtmlReporter = function(options) { failures.push(failure); } + + if(result.status == "pending") { + pendingSpecCount++; + } }; this.jasmineDone = function() { @@ -116,8 +121,10 @@ jasmine.HtmlReporter = function(options) { ) ); } - var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount), - statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); + var statusBarMessage = "" + pluralize("spec", specsExecuted) + ", " + pluralize("failure", failureCount); + if(pendingSpecCount) { statusBarMessage += ", " + pluralize("pending spec", pendingSpecCount); } + + var statusBarClassName = "bar " + ((failureCount > 0) ? "failed" : "passed"); alert.appendChild(createDom("span", {className: statusBarClassName}, statusBarMessage)); var results = find(".results")[0]; diff --git a/src/html/_HTMLReporter.scss b/src/html/_HTMLReporter.scss index a9d84fcc..0e8e93e2 100644 --- a/src/html/_HTMLReporter.scss +++ b/src/html/_HTMLReporter.scss @@ -14,6 +14,7 @@ $passing-color: #5e7d00; $light-failing-color: #cf867e; $failing-color: #b03911; +$pending-color: #ba9d37; $neutral-color: #bababa; @@ -120,21 +121,20 @@ body { } } - &.disabled { - font-size: 14px; + &.disabled { + font-size: 14px; - &:before { - color: $neutral-color; - content: "\02022"; + &:before { + color: $neutral-color; + content: "\02022"; + } } - } &.pending { - line-height: ($line-height / 2) + 4; - + line-height: 17px; &:before { - color: $faint-text-color; - content: "-"; + color: $pending-color; + content: "*"; } } } @@ -264,6 +264,10 @@ body { &.failed a { color: $failing-color; } + + &.pending a { + color: $pending-color; + } } } diff --git a/src/html/jasmine.css b/src/html/jasmine.css index 603ac73b..3196e974 100644 --- a/src/html/jasmine.css +++ b/src/html/jasmine.css @@ -18,8 +18,8 @@ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } .html-reporter .symbol-summary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; } .html-reporter .symbol-summary li.disabled { font-size: 14px; } .html-reporter .symbol-summary li.disabled:before { color: #bababa; content: "\02022"; } -.html-reporter .symbol-summary li.pending { line-height: 11px; } -.html-reporter .symbol-summary li.pending:before { color: #aaaaaa; content: "-"; } +.html-reporter .symbol-summary li.pending { line-height: 17px; } +.html-reporter .symbol-summary li.pending:before { color: #ba9d37; content: "*"; } .html-reporter .exceptions { color: #fff; float: right; margin-top: 5px; margin-right: 5px; } .html-reporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; } .html-reporter .bar.failed { background-color: #b03911; } @@ -43,6 +43,7 @@ body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; } .html-reporter .summary ul.suite { margin-top: 7px; margin-bottom: 7px; } .html-reporter .summary li.passed a { color: #5e7d00; } .html-reporter .summary li.failed a { color: #b03911; } +.html-reporter .summary li.pending a { color: #ba9d37; } .html-reporter .description + .suite { margin-top: 0; } .html-reporter .suite { margin-top: 14px; } .html-reporter .suite a { color: #333333; }