Re-add async support (achieved via done callbacks)

- TODO: pull out queueRunner into a new object.
This commit is contained in:
Davis W. Frank & Rajan Agaskar
2012-12-07 16:30:32 -08:00
parent c2e1327f39
commit a526ebf261
5 changed files with 226 additions and 141 deletions

View File

@@ -38,9 +38,11 @@
return env.spyOn(obj, methodName); return env.spyOn(obj, methodName);
}, },
clock: function() { clock: env.clock,
return env.clock; setTimeout: env.clock.setTimeout,
}, clearTimeout: env.clock.clearTimeout,
setInterval: env.clock.setInterval,
clearInterval: env.clock.clearInterval,
jsApiReporter: new jasmine.JsApiReporter(jasmine) jsApiReporter: new jasmine.JsApiReporter(jasmine)
}; };

View File

@@ -1958,47 +1958,60 @@ jasmine.Spec.prototype.expect = function(actual) {
}; };
jasmine.Spec.prototype.execute = function() { jasmine.Spec.prototype.execute = function() {
var self = this;
if (this.disabled) { if (this.disabled) {
resultCallback.call(this); resultCallback();
return; return;
} }
var befores = this.beforeFns() || [], var befores = this.beforeFns() || [],
afters = this.afterFns() || []; afters = this.afterFns() || [];
this.startCallback(this); this.startCallback(this);
try { var allFns = befores.concat(this.fn).concat(afters);
for (var i = 0; i < befores.length; i++) {
befores[i].call(this); queueRunner(allFns, 0);
}
this.fn.call(this); function attempt(fn) {
for (i = 0; i < afters.length; i++) { try {
afters[i].call(this); fn();
} } catch (e) {
} catch (e) { //TODO: weird. buildExpectationResult is really a presenter for expectations
//TODO: weird. buildExpectationResult is really a presenter for expectations //so this should take an expectation object.
//so this should take an expectation object. self.addExpectationResult(false, self.expectationResultFactory({
this.addExpectationResult(false, this.expectationResultFactory({ matcherName: "",
matcherName: "", passed: false,
passed: false, expected: "",
expected: "", actual: "",
actual: "", message: self.exceptionFormatter(e),
message: this.exceptionFormatter(e), trace: e
trace: e }));
})); if (!self.catchExceptions) {
if (!this.catchExceptions) { resultCallback();
throw e; throw e;
}
} }
} }
finally {
resultCallback.call(this); function queueRunner(allFns, index) {
if (index >= allFns.length) {
resultCallback();
return;
}
var fn = allFns[index];
if (fn.length > 0) {
attempt(function() { fn.call(self, function() { queueRunner(allFns, index + 1) }) });
} else {
attempt(function() { fn.call(self); });
queueRunner(allFns, index + 1);
}
} }
function resultCallback() { function resultCallback() {
this.resultCallback({ self.resultCallback({
id: this.id, id: self.id,
status: this.status(), status: self.status(),
description: this.description, description: self.description,
failedExpectations: this.failedExpectations failedExpectations: self.failedExpectations
}); });
} }
}; };

View File

@@ -1,23 +1,23 @@
describe("Spec (integration)", function() { describe("Spec", function() {
it("reports results for passing tests", function() { it("reports results for passing tests", function() {
var resultCallback = jasmine.createSpy('resultCallback'), var resultCallback = originalJasmine.createSpy('resultCallback'),
expectationFactory = function(actual, spec) { expectationFactory = function(actual, spec) {
expect(actual).toBe('some-actual'); expect(actual).toBe('some-actual');
return { return {
pass: function() { pass: function() {
spec.addExpectationResult(true); spec.addExpectationResult(true);
}
} }
}
},
spec = new jasmine.Spec({
description: 'my test',
id: 'some-id',
fn: function() {
this.expect('some-actual').pass();
}, },
spec = new jasmine.Spec({ resultCallback: resultCallback,
description: 'my test', expectationFactory: expectationFactory
id: 'some-id', });
fn: function() {
this.expect('some-actual').pass();
},
resultCallback: resultCallback,
expectationFactory: expectationFactory
});
spec.execute(); spec.execute();
@@ -29,24 +29,24 @@ describe("Spec (integration)", function() {
}); });
}); });
it("reports results for failing tests", function() { it("reports results for failing tests", function() {
var resultCallback = jasmine.createSpy('resultCallback'), var resultCallback = originalJasmine.createSpy('resultCallback'),
expectationFactory = function(actual, spec) { expectationFactory = function(actual, spec) {
expect(actual).toBe('some-actual'); expect(actual).toBe('some-actual');
return { return {
fail: function() { fail: function() {
spec.addExpectationResult(true); spec.addExpectationResult(true);
}
} }
}
},
spec = new jasmine.Spec({
description: 'my test',
id: 'some-id',
fn: function() {
this.expect('some-actual').fail();
}, },
spec = new jasmine.Spec({ resultCallback: resultCallback,
description: 'my test', expectationFactory: expectationFactory
id: 'some-id', });
fn: function() {
this.expect('some-actual').fail();
},
resultCallback: resultCallback,
expectationFactory: expectationFactory
});
spec.execute(); spec.execute();
@@ -58,14 +58,13 @@ describe("Spec (integration)", function() {
}); });
}); });
//TODO: test order of befores, spec, after.
it("executes before fns, after fns", function() { it("executes before fns, after fns", function() {
var before = jasmine.createSpy('before'), var before = originalJasmine.createSpy('before'),
after = jasmine.createSpy('after'), after = originalJasmine.createSpy('after'),
fn = originalJasmine.createSpy('test body').andCallFake(function() { fn = originalJasmine.createSpy('test body').andCallFake(function() {
expect(before).toHaveBeenCalled(); expect(before).toHaveBeenCalled();
expect(after).not.toHaveBeenCalled(); expect(after).not.toHaveBeenCalled();
}); });
spec = new jasmine.Spec({ spec = new jasmine.Spec({
fn: fn, fn: fn,
beforeFns: function() { beforeFns: function() {
@@ -86,9 +85,61 @@ describe("Spec (integration)", function() {
expect(after).toHaveBeenCalled(); expect(after).toHaveBeenCalled();
expect(after.mostRecentCall.object).toBe(spec); expect(after.mostRecentCall.object).toBe(spec);
}); });
});
describe("Spec (real-ish unit tests)", function() { it("passes an optional callback to spec bodies, befores, and afters", function() {
//TODO: it would be nice if spy arity could match the fake, so we could do something like:
//createSpy('asyncfn').andCallFake(function(done) {});
var resultCallback = originalJasmine.createSpy('resultCallback'),
beforeCallback = originalJasmine.createSpy('beforeCallback'),
fnCallback = originalJasmine.createSpy('fnCallback'),
afterCallback = originalJasmine.createSpy('afterCallback'),
before = function(done) {
beforeCallback();
setTimeout(function() { done() }, 100);
},
fn = function(done) {
fnCallback();
setTimeout(function() { done() }, 100);
},
after = function(done) {
afterCallback();
setTimeout(function() { done() }, 100);
},
spec = new jasmine.Spec({
resultCallback: resultCallback,
fn: fn,
beforeFns: function() {
return [before]
},
afterFns: function() {
return [after]
}
});
clock.install();
spec.execute();
expect(beforeCallback).toHaveBeenCalled();
expect(fnCallback).not.toHaveBeenCalled();
expect(afterCallback).not.toHaveBeenCalled();
expect(resultCallback).not.toHaveBeenCalled();
clock.tick(100);
expect(fnCallback).toHaveBeenCalled();
expect(afterCallback).not.toHaveBeenCalled();
expect(resultCallback).not.toHaveBeenCalled();
clock.tick(100);
expect(afterCallback).toHaveBeenCalled();
expect(resultCallback).not.toHaveBeenCalled();
clock.tick(100);
expect(resultCallback).toHaveBeenCalled();
});
it("status returns null by default", function() { it("status returns null by default", function() {
var spec = new jasmine.Spec({}); var spec = new jasmine.Spec({});
expect(spec.status()).toBeNull(); expect(spec.status()).toBeNull();
@@ -110,13 +161,13 @@ describe("Spec (real-ish unit tests)", function() {
it("calls the resultCallback with a failure when an exception occurs in the spec fn", function() { it("calls the resultCallback with a failure when an exception occurs in the spec fn", function() {
//TODO: one day we should pass a stack with this. //TODO: one day we should pass a stack with this.
var resultCallback = originalJasmine.createSpy('resultCallback'), var resultCallback = originalJasmine.createSpy('resultCallback'),
spec = new jasmine.Spec({ spec = new jasmine.Spec({
fn: function() { fn: function() {
throw new Error(); throw new Error();
}, },
catchExceptions: true, catchExceptions: true,
resultCallback: resultCallback resultCallback: resultCallback
}); });
expect(resultCallback).not.toHaveBeenCalled(); expect(resultCallback).not.toHaveBeenCalled();
spec.execute(); spec.execute();
@@ -129,13 +180,13 @@ describe("Spec (real-ish unit tests)", function() {
it("throws when an exception occurs in the spec fn if catchExceptions is false", function() { it("throws when an exception occurs in the spec fn if catchExceptions is false", function() {
//TODO: one day we should pass a stack with this. //TODO: one day we should pass a stack with this.
var resultCallback = originalJasmine.createSpy('resultCallback'), var resultCallback = originalJasmine.createSpy('resultCallback'),
spec = new jasmine.Spec({ spec = new jasmine.Spec({
fn: function() { fn: function() {
throw new Error(); throw new Error();
}, },
catchExceptions: false, catchExceptions: false,
resultCallback: resultCallback resultCallback: resultCallback
}); });
expect(function() { expect(function() {
spec.execute(); spec.execute();
@@ -144,22 +195,22 @@ describe("Spec (real-ish unit tests)", function() {
it("should call the start callback before any befores are called", function() { it("should call the start callback before any befores are called", function() {
var beforesWereCalled = false, var beforesWereCalled = false,
startCallback = originalJasmine.createSpy('start-callback').andCallFake(function() { startCallback = originalJasmine.createSpy('start-callback').andCallFake(function() {
expect(beforesWereCalled).toBe(false); expect(beforesWereCalled).toBe(false);
}), }),
spec = new jasmine.Spec({ spec = new jasmine.Spec({
fn: function() { fn: function() {
}, },
beforeFns: function() { beforeFns: function() {
return [function() { return [function() {
beforesWereCalled = true beforesWereCalled = true
}] }]
}, },
startCallback: startCallback, startCallback: startCallback,
catchExceptions: false, catchExceptions: false,
resultCallback: function() { resultCallback: function() {
} }
}); });
spec.execute(); spec.execute();
expect(startCallback).toHaveBeenCalled(); expect(startCallback).toHaveBeenCalled();
@@ -177,15 +228,15 @@ describe("Spec (real-ish unit tests)", function() {
}); });
it("can be disabled", function() { it("can be disabled", function() {
var startCallback = jasmine.createSpy('startCallback'), var startCallback = originalJasmine.createSpy('startCallback'),
specBody = jasmine.createSpy('specBody'), specBody = originalJasmine.createSpy('specBody'),
resultCallback = jasmine.createSpy('resultCallback'), resultCallback = originalJasmine.createSpy('resultCallback'),
spec = new jasmine.Spec({ spec = new jasmine.Spec({
startCallback: startCallback, startCallback: startCallback,
fn: specBody, fn: specBody,
resultCallback: resultCallback resultCallback: resultCallback
}); });
spec.disable(); spec.disable();
expect(spec.status()).toBe('disabled'); expect(spec.status()).toBe('disabled');
@@ -198,3 +249,4 @@ describe("Spec (real-ish unit tests)", function() {
expect(resultCallback).toHaveBeenCalled(); expect(resultCallback).toHaveBeenCalled();
}); });
}); });

View File

@@ -49,6 +49,11 @@ var jasmineInterface = {
return env.spyOn(obj, methodName); return env.spyOn(obj, methodName);
}, },
clock: env.clock,
setTimeout: env.clock.setTimeout,
clearTimeout: env.clock.clearTimeout,
setInterval: env.clock.setInterval,
clearInterval: env.clock.clearInterval,
jsApiReporter: new jasmine.JsApiReporter(jasmine) jsApiReporter: new jasmine.JsApiReporter(jasmine)
}; };

View File

@@ -27,47 +27,60 @@ jasmine.Spec.prototype.expect = function(actual) {
}; };
jasmine.Spec.prototype.execute = function() { jasmine.Spec.prototype.execute = function() {
var self = this;
if (this.disabled) { if (this.disabled) {
resultCallback.call(this); resultCallback();
return; return;
} }
var befores = this.beforeFns() || [], var befores = this.beforeFns() || [],
afters = this.afterFns() || []; afters = this.afterFns() || [];
this.startCallback(this); this.startCallback(this);
try { var allFns = befores.concat(this.fn).concat(afters);
for (var i = 0; i < befores.length; i++) {
befores[i].call(this); queueRunner(allFns, 0);
}
this.fn.call(this); function attempt(fn) {
for (i = 0; i < afters.length; i++) { try {
afters[i].call(this); fn();
} } catch (e) {
} catch (e) { //TODO: weird. buildExpectationResult is really a presenter for expectations
//TODO: weird. buildExpectationResult is really a presenter for expectations //so this should take an expectation object.
//so this should take an expectation object. self.addExpectationResult(false, self.expectationResultFactory({
this.addExpectationResult(false, this.expectationResultFactory({ matcherName: "",
matcherName: "", passed: false,
passed: false, expected: "",
expected: "", actual: "",
actual: "", message: self.exceptionFormatter(e),
message: this.exceptionFormatter(e), trace: e
trace: e }));
})); if (!self.catchExceptions) {
if (!this.catchExceptions) { resultCallback();
throw e; throw e;
}
} }
} }
finally {
resultCallback.call(this); function queueRunner(allFns, index) {
if (index >= allFns.length) {
resultCallback();
return;
}
var fn = allFns[index];
if (fn.length > 0) {
attempt(function() { fn.call(self, function() { queueRunner(allFns, index + 1) }) });
} else {
attempt(function() { fn.call(self); });
queueRunner(allFns, index + 1);
}
} }
function resultCallback() { function resultCallback() {
this.resultCallback({ self.resultCallback({
id: this.id, id: self.id,
status: this.status(), status: self.status(),
description: this.description, description: self.description,
failedExpectations: this.failedExpectations failedExpectations: self.failedExpectations
}); });
} }
}; };