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; }