* Removed old Queue & Runner in favor of Suite using the new QueueRunner

* New reporter interface across all reporters
* xdescribe & xit now store disabled specs
* Rewrite of HtmlReporter to support new interface and be more performant
This commit is contained in:
Davis W. Frank
2012-12-10 22:43:03 -08:00
committed by Dan Hansen and Davis W. Frank
parent 05977203a6
commit 3fc79bac9e
43 changed files with 3229 additions and 3318 deletions

View File

@@ -1,19 +1,19 @@
jasmine.Clock = function(global, delayedFunctionScheduler) {
var self = this,
realTimingFunctions = {
setTimeout: global.setTimeout,
clearTimeout: global.clearTimeout,
setInterval: global.setInterval,
clearInterval: global.clearInterval
},
fakeTimingFunctions = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval
},
timer = realTimingFunctions,
installed = false;
realTimingFunctions = {
setTimeout: global.setTimeout,
clearTimeout: global.clearTimeout,
setInterval: global.setInterval,
clearInterval: global.clearInterval
},
fakeTimingFunctions = {
setTimeout: setTimeout,
clearTimeout: clearTimeout,
setInterval: setInterval,
clearInterval: clearInterval
},
timer = realTimingFunctions,
installed = false;
self.install = function() {
installed = true;
@@ -72,7 +72,7 @@ jasmine.Clock = function(global, delayedFunctionScheduler) {
}
function setTimeout(fn, delay) {
return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments,2));
return delayedFunctionScheduler.scheduleFunction(fn, delay, argSlice(arguments, 2));
}
function clearTimeout(id) {

View File

@@ -1,8 +1,3 @@
/**
* Environment for Jasmine
*
* @constructor
*/
(function() {
jasmine.Env = function(options) {
options = options || {};
@@ -10,21 +5,23 @@
var global = options.global || jasmine.getGlobal();
var catchExceptions = true;
var encourageGC = options.encourageGarbageCollection || encourageGarbageCollection;
this.clock = new jasmine.Clock(global, new jasmine.DelayedFunctionScheduler());
var suiteConstructor = jasmine.Suite;
var isSuite = function(thing) {
return thing instanceof suiteConstructor;
};
this.jasmine = jasmine;
this.currentRunner_ = new jasmine.Runner(this, isSuite);
this.spies_ = [];
this.currentSpec = null;
this.undefined = jasmine.undefined;
this.reporter = new jasmine.MultiReporter();
this.reporter = new jasmine.ReportDispatcher([
"jasmineStarted",
"jasmineDone",
"suiteStarted",
"suiteDone",
"specStarted",
"specDone"
]);
this.lastUpdate = 0;
this.specFilter = function() {
@@ -49,18 +46,18 @@
return expect;
};
var startCallback = function(spec) {
var specStarted = function(spec) {
self.currentSpec = spec;
self.reporter.reportSpecStarting(spec);
self.reporter.specStarted(spec.result);
};
var beforeFns = function(currentSuite) {
return function() {
var befores = [];
for (var suite = currentSuite; suite; suite = suite.parentSuite) {
befores = befores.concat(suite.before_)
befores = befores.concat(suite.beforeFns)
}
return befores.concat(self.currentRunner_.before_).reverse();
return befores.reverse();
}
};
@@ -68,9 +65,9 @@
return function() {
var afters = [];
for (var suite = currentSuite; suite; suite = suite.parentSuite) {
afters = afters.concat(suite.after_)
afters = afters.concat(suite.afterFns)
}
return afters.concat(self.currentRunner_.after_)
return afters;
}
};
@@ -87,60 +84,14 @@
return buildExpectationResult(attrs);
};
// TODO: fix this naming, and here's where the value comes in
this.catchExceptions = function(value) {
return catchExceptions = !!value;
};
this.catchingExceptions = function(value) {
catchExceptions = !!value;
return catchExceptions;
};
this.specFactory = function(description, fn, suite) {
var spec = new specConstructor({
id: self.nextSpecId(),
beforeFns: beforeFns(suite),
afterFns: afterFns(suite),
expectationFactory: expectationFactory,
exceptionFormatter: exceptionFormatter,
resultCallback: specResultCallback,
getSpecName: function(spec) {
return getSpecName(spec, suite)
},
startCallback: startCallback,
description: description,
catchingExceptions: this.catchingExceptions,
expectationResultFactory: expectationResultFactory,
fn: fn
});
if (!self.specFilter(spec)) {
spec.disable();
}
return spec;
function specResultCallback(result) {
self.clock.uninstall();
self.currentSpec = null;
encourageGC(function() {
suite.specComplete(result);
});
}
};
var queueConstructor = jasmine.Queue;
var queueFactory = function() {
return new queueConstructor(self);
};
this.suiteFactory = function(description) {
return new suiteConstructor({
env: self,
description: description,
currentSuite: self.currentSuite,
queueFactory: queueFactory,
isSuite: isSuite
});
this.catchingExceptions = function() {
return catchExceptions;
};
var maximumSpecCallbackDepth = 100;
@@ -155,6 +106,86 @@
fn();
}
}
var queueRunnerFactory = function(options) {
options.catchingExceptions = self.catchingExceptions;
options.encourageGC = options.encourageGarbageCollection || encourageGarbageCollection;
new jasmine.QueueRunner(options).run(options.fns, 0);
};
var totalSpecsDefined = 0;
this.specFactory = function(description, fn, suite) {
totalSpecsDefined++;
var spec = new specConstructor({
id: self.nextSpecId(),
beforeFns: beforeFns(suite),
afterFns: afterFns(suite),
expectationFactory: expectationFactory,
exceptionFormatter: exceptionFormatter,
resultCallback: specResultCallback,
getSpecName: function(spec) {
return getSpecName(spec, suite)
},
onStart: specStarted,
description: description,
expectationResultFactory: expectationResultFactory,
queueRunner: queueRunnerFactory,
fn: fn
});
if (!self.specFilter(spec)) {
spec.disable();
}
return spec;
function specResultCallback(result) {
self.removeAllSpies();
self.clock.uninstall();
self.currentSpec = null;
self.reporter.specDone(result);
}
};
var suiteStarted = function(suite) {
self.reporter.suiteStarted(suite.result);
};
var suiteConstructor = jasmine.Suite;
this.topSuite = new jasmine.Suite({
env: this,
id: this.nextSuiteId(),
description: 'Jasmine__TopLevel__Suite',
queueRunner: queueRunnerFactory,
completeCallback: function() {}, // TODO - hook this up
resultCallback: function() {} // TODO - hook this up
});
this.currentSuite = this.topSuite;
this.suiteFactory = function(description) {
return new suiteConstructor({
env: self,
id: self.nextSuiteId(),
description: description,
parentSuite: self.currentSuite,
queueRunner: queueRunnerFactory,
onStart: suiteStarted,
resultCallback: function(attrs) {
self.reporter.suiteDone(attrs);
}
});
};
this.execute = function() {
this.reporter.jasmineStarted({
totalSpecsDefined: totalSpecsDefined
});
this.topSuite.execute(this.reporter.jasmineDone);
};
};
//TODO: shim Spec addMatchers behavior into Env. Should be rewritten to remove globals, etc.
@@ -167,9 +198,7 @@
jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
this.matchersClass = newMatchersClass;
};
/**
* @returns an object containing jasmine version build info, if set.
*/
jasmine.Env.prototype.version = function() {
if (this.jasmine.version_) {
return this.jasmine.version_;
@@ -208,6 +237,7 @@
return spyObj;
};
// TODO: move this to closure
jasmine.Env.prototype.removeAllSpies = function() {
for (var i = 0; i < this.spies_.length; i++) {
var spy = this.spies_[i];
@@ -215,9 +245,8 @@
}
this.spies_ = [];
};
/**
* @returns string containing jasmine version build info, if set.
*/
// TODO: move this to closure
jasmine.Env.prototype.versionString = function() {
if (!this.jasmine.version_) {
return "version unknown";
@@ -232,42 +261,27 @@
return versionString;
};
/**
* @returns a sequential integer starting at 0
*/
// TODO: move this to closure
jasmine.Env.prototype.nextSpecId = function() {
return this.nextSpecId_++;
};
/**
* @returns a sequential integer starting at 0
*/
// TODO: move this to closure
jasmine.Env.prototype.nextSuiteId = function() {
return this.nextSuiteId_++;
};
/**
* Register a reporter to receive status updates from Jasmine.
* @param {jasmine.Reporter} reporter An object which will receive status updates.
*/
// TODO: move this to closure
jasmine.Env.prototype.addReporter = function(reporter) {
this.reporter.addReporter(reporter);
};
jasmine.Env.prototype.execute = function() {
this.currentRunner_.execute();
};
// TODO: move this to closure
jasmine.Env.prototype.describe = function(description, specDefinitions) {
var suite = this.suiteFactory(description, specDefinitions);
var parentSuite = this.currentSuite;
if (parentSuite) {
parentSuite.add(suite);
} else {
this.currentRunner_.addSuite(suite);
}
parentSuite.addSuite(suite);
this.currentSuite = suite;
var declarationError = null;
@@ -288,46 +302,40 @@
return suite;
};
jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
if (this.currentSuite) {
this.currentSuite.beforeEach(beforeEachFunction);
} else {
this.currentRunner_.beforeEach(beforeEachFunction);
}
};
jasmine.Env.prototype.currentRunner = function() {
return this.currentRunner_;
};
jasmine.Env.prototype.afterEach = function(afterEachFunction) {
if (this.currentSuite) {
this.currentSuite.afterEach(afterEachFunction);
} else {
this.currentRunner_.afterEach(afterEachFunction);
}
};
jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
return {
execute: function() {
}
};
// TODO: move this to closure
jasmine.Env.prototype.xdescribe = function(description, specDefinitions) {
var suite = this.describe(description, specDefinitions);
suite.disable();
return suite;
};
// TODO: move this to closure
jasmine.Env.prototype.it = function(description, fn) {
var spec = this.specFactory(description, fn, this.currentSuite);
this.currentSuite.add(spec);
this.currentSuite.addSpec(spec);
return spec;
};
jasmine.Env.prototype.xit = function(desc, func) {
return {
id: this.nextSpecId(),
runs: function() {
}
};
// TODO: move this to closure
jasmine.Env.prototype.xit = function(description, fn) {
var spec = this.it(description, fn);
spec.disable();
return spec;
};
// TODO: move this to closure
jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
this.currentSuite.beforeEach(beforeEachFunction);
};
// TODO: move this to closure
jasmine.Env.prototype.afterEach = function(afterEachFunction) {
this.currentSuite.afterEach(afterEachFunction);
};
// TODO: Still needed?
jasmine.Env.prototype.currentRunner = function() {
return this.topSuite;
};
jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {

View File

@@ -1,104 +1,60 @@
/** JavaScript API reporter.
*
* @constructor
*/
jasmine.JsApiReporter = function(jasmine) {
this.jasmine = jasmine || {};
this.started = false;
this.finished = false;
this.suites_ = [];
this.results_ = {};
};
jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {
this.started = true;
var suites = runner.topLevelSuites();
for (var i = 0; i < suites.length; i++) {
var suite = suites[i];
this.suites_.push(this.summarize_(suite));
}
};
var status = 'loaded';
jasmine.JsApiReporter.prototype.suites = function() {
return this.suites_;
};
jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
var isSuite = suiteOrSpec instanceof this.jasmine.Suite;
var summary = {
id: suiteOrSpec.id,
name: suiteOrSpec.description,
type: isSuite ? 'suite' : 'spec',
children: []
this.jasmineStarted = function() {
this.started = true;
status = 'started';
};
if (isSuite) {
var children = suiteOrSpec.children();
for (var i = 0; i < children.length; i++) {
summary.children.push(this.summarize_(children[i]));
}
}
return summary;
};
jasmine.JsApiReporter.prototype.results = function() {
return this.results_;
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
this.finished = true;
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSpecResults = function(result) {
this.results_[result.id] = {
messages: result.failedExpectations,
//result is status
result: result.status
this.jasmineDone = function() {
this.finished = true;
status = 'done';
};
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.log = function(str) {
};
//TODO: make work with new presenter.
jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){
var results = {};
for (var i = 0; i < specIds.length; i++) {
var specId = specIds[i];
results[specId] = this.summarizeResult_(this.results_[specId]);
}
return results;
};
jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
var summaryMessages = [];
var messagesLength = result.messages.length;
for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {
var resultMessage = result.messages[messageIndex];
//TODO: use result presenter here, not a bunch of spec crap
summaryMessages.push({
//TODO: remove text.
text: resultMessage.type == 'log' ? resultMessage.toString() : this.jasmine.undefined,
//TODO: wat? in theory this is saying non-expect results should always be considered passed, but that's weird.
passed: resultMessage.passed || true, //status === 'passed'
type: resultMessage.type,
message: resultMessage.message,
trace: {
stack: !resultMessage.passed ? resultMessage.trace.stack : this.jasmine.undefined
}
});
}
return {
result : result.result,
messages : summaryMessages
this.status = function() {
return status;
};
};
var suites = {};
this.suiteStarted = function(result) {
storeSuite(result);
};
this.suiteDone = function(result) {
storeSuite(result);
};
function storeSuite(result) {
suites[result.id] = result;
}
this.suites = function() {
return suites;
};
var specs = {};
this.specStarted = function(result) {
storeSpec(result);
};
this.specDone = function(result) {
storeSpec(result);
};
function storeSpec(result) {
specs[result.id] = result;
}
this.specs = function() {
return specs;
};
};

View File

@@ -1,35 +0,0 @@
/**
* @constructor
*/
jasmine.MultiReporter = function() {
this.subReporters_ = [];
};
jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);
jasmine.MultiReporter.prototype.addReporter = function(reporter) {
this.subReporters_.push(reporter);
};
(function() {
var functionNames = [
"reportRunnerStarting",
"reportRunnerResults",
"reportSuiteResults",
"reportSpecStarting",
"reportSpecResults",
"log"
];
for (var i = 0; i < functionNames.length; i++) {
var functionName = functionNames[i];
jasmine.MultiReporter.prototype[functionName] = (function(functionName) {
return function() {
for (var j = 0; j < this.subReporters_.length; j++) {
var subReporter = this.subReporters_[j];
if (subReporter[functionName]) {
subReporter[functionName].apply(subReporter, arguments);
}
}
};
})(functionName);
}
})();

View File

@@ -1,111 +0,0 @@
jasmine.Queue = function(env) {
this.env = env;
// parallel to blocks. each true value in this array means the block will
// get executed even if we abort
this.ensured = [];
this.blocks = [];
this.running = false;
this.index = 0;
this.offset = 0;
this.abort = false;
};
jasmine.Queue.prototype.addBefore = function(block, ensure) {
if (ensure === this.env.undefined) {
ensure = false;
}
this.blocks.unshift(block);
this.ensured.unshift(ensure);
};
jasmine.Queue.prototype.add = function(block, ensure) {
if (ensure === this.env.undefined) {
ensure = false;
}
this.blocks.push(block);
this.ensured.push(ensure);
};
jasmine.Queue.prototype.insertNext = function(block, ensure) {
if (ensure === this.env.undefined) {
ensure = false;
}
this.ensured.splice((this.index + this.offset + 1), 0, ensure);
this.blocks.splice((this.index + this.offset + 1), 0, block);
this.offset++;
};
jasmine.Queue.prototype.start = function(onComplete) {
this.running = true;
this.onComplete = onComplete;
this.next_();
};
jasmine.Queue.prototype.isRunning = function() {
return this.running;
};
jasmine.Queue.LOOP_DONT_RECURSE = true;
jasmine.Queue.prototype.incrementQueue = function() {
if (this.blocks[this.index].abort) {
this.abort = true;
}
this.offset = 0;
this.index++;
this.next_();
}
jasmine.Queue.prototype.next_ = function() {
var self = this;
// var goAgain = true;
// while (goAgain) {
// goAgain = false;
if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {
// var calledSynchronously = true;
// var completedSynchronously = false;
// var onComplete = function () {
// if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
// completedSynchronously = true;
// return;
// }
self.blocks[self.index].execute(function() { self.incrementQueue() });
// var now = new Date().getTime();
// if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {
// self.env.lastUpdate = now;
// self.env.setTimeout(function() {
// self.next_();
// }, 0);
// } else {
// if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {
// goAgain = true;
// } else {
// self.next_();
// }
// }
// };
// self.blocks[self.index].execute(function() { self.next_(); });
// calledSynchronously = false;
// if (completedSynchronously) {
// onComplete();
// }
} else {
self.running = false;
if (self.onComplete) {
self.onComplete();
}
}
// }
};

40
src/core/QueueRunner.js Normal file
View File

@@ -0,0 +1,40 @@
jasmine.QueueRunner = function(attrs) {
this.fns = attrs.fns || [];
this.onComplete = attrs.onComplete || function() {};
this.encourageGC = attrs.encourageGC || function(fn) {fn()};
this.onException = attrs.onException || function() {};
this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
};
jasmine.QueueRunner.prototype.execute = function() {
this.run(this.fns, 0)
};
jasmine.QueueRunner.prototype.run = function(fns, index) {
if (index >= fns.length) {
this.encourageGC(this.onComplete);
return;
}
var fn = fns[index];
var self = this;
if (fn.length > 0) {
attempt(function() { fn.call(self, function() { self.run(fns, index + 1) }) });
} else {
attempt(function() { fn.call(self); });
self.run(fns, index + 1);
}
function attempt(fn) {
try {
fn();
} catch (e) {
self.onException(e);
if (!self.catchingExceptions()) {
//TODO: set a var when we catch an exception and
//use a finally block to close the loop in a nice way..
throw e;
}
}
}
};

View File

@@ -0,0 +1,30 @@
jasmine.ReportDispatcher = function(methods) {
var dispatchedMethods = methods || [];
for (var i = 0; i < dispatchedMethods.length; i++) {
var method = dispatchedMethods[i];
this[method] = function(m) {
return function() {
dispatch(m, arguments);
};
}(method);
}
var reporters = [];
this.addReporter = function(reporter) {
reporters.push(reporter);
};
return this;
function dispatch(method, args) {
for (var i = 0; i < reporters.length; i++) {
var reporter = reporters[i];
if (reporter[method]) {
reporter[method].apply(reporter, args);
}
}
}
};

View File

@@ -1,31 +0,0 @@
/** No-op base class for Jasmine reporters.
*
* @constructor
*/
jasmine.Reporter = function() {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportRunnerResults = function(runner) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSuiteResults = function(suite) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecStarting = function(spec) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.reportSpecResults = function(spec) {
};
//noinspection JSUnusedLocalSymbols
jasmine.Reporter.prototype.log = function(str) {
};

View File

@@ -1,80 +0,0 @@
//TODO: runner is a special case of suite.
/**
* Runner
*
* @constructor
* @param {jasmine.Env} env
*/
jasmine.Runner = function(env, isSuite) {
var self = this;
self.env = env;
self.queue = new jasmine.Queue(env);
self.before_ = [];
self.after_ = [];
self.suites_ = [];
self.isSuite = isSuite || function() {};
};
jasmine.Runner.prototype.execute = function() {
var self = this;
if (self.env.reporter.reportRunnerStarting) {
self.env.reporter.reportRunnerStarting(this);
}
self.queue.start(function () {
self.finishCallback();
});
};
jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {
beforeEachFunction.typeName = 'beforeEach';
this.before_.splice(0,0,beforeEachFunction);
};
jasmine.Runner.prototype.afterEach = function(afterEachFunction) {
afterEachFunction.typeName = 'afterEach';
this.after_.splice(0,0,afterEachFunction);
};
jasmine.Runner.prototype.finishCallback = function() {
this.env.reporter.reportRunnerResults(this);
};
jasmine.Runner.prototype.addSuite = function(suite) {
this.suites_.push(suite);
this.queue.add(suite);
};
//TODO: runner should die a slow unhappy death.
//Nobody should ever call instanceof.
jasmine.Runner.prototype.add = function(block) {
if (this.isSuite(block)) {
this.addSuite(block);
} else {
this.queue.add(block);
}
};
jasmine.Runner.prototype.specs = function () {
var suites = this.suites();
var specs = [];
for (var i = 0; i < suites.length; i++) {
specs = specs.concat(suites[i].specs());
}
return specs;
};
jasmine.Runner.prototype.suites = function() {
return this.suites_;
};
jasmine.Runner.prototype.topLevelSuites = function() {
var topLevelSuites = [];
for (var i = 0; i < this.suites_.length; i++) {
if (!this.suites_[i].parentSuite) {
topLevelSuites.push(this.suites_[i]);
}
}
return topLevelSuites;
};

View File

@@ -1,24 +1,33 @@
jasmine.Spec = function(attrs) {
this.failedExpectations = [];
this.encounteredExpectations = false;
this.expectationFactory = attrs.expectationFactory;
this.resultCallback = attrs.resultCallback || function() {};
this.resultCallback = attrs.resultCallback || function() {};
this.id = attrs.id;
this.description = attrs.description;
this.description = attrs.description || '';
this.fn = attrs.fn;
this.beforeFns = attrs.beforeFns || function() {};
this.afterFns = attrs.afterFns || function() {};
this.catchingExceptions = attrs.catchingExceptions;
this.startCallback = attrs.startCallback || function() {};
this.onStart = attrs.onStart || function() {};
this.exceptionFormatter = attrs.exceptionFormatter || function() {};
this.getSpecName = attrs.getSpecName;
this.getSpecName = attrs.getSpecName || function() { return ''; };
this.expectationResultFactory = attrs.expectationResultFactory || function() {};
this.queueRunner = attrs.queueRunner || { execute: function() {}};
this.catchingExceptions = attrs.catchingExceptions || function() { return true; };
this.result = {
id: this.id,
description: this.description,
fullName: this.getFullName(),
status: this.status(),
failedExpectations: []
};
};
jasmine.Spec.prototype.addExpectationResult = function(passed, data) {
this.encounteredExpectations = true;
if (!passed) {
this.failedExpectations.push(data);
this.result.failedExpectations.push(data);
}
};
@@ -26,26 +35,22 @@ jasmine.Spec.prototype.expect = function(actual) {
return this.expectationFactory(actual, this);
};
jasmine.Spec.prototype.execute = function() {
jasmine.Spec.prototype.execute = function(onComplete) {
var self = this;
if (this.disabled) {
resultCallback();
complete();
return;
}
var befores = this.beforeFns() || [],
afters = this.afterFns() || [];
this.startCallback(this);
afters = this.afterFns() || [];
var allFns = befores.concat(this.fn).concat(afters);
queueRunner(allFns, 0);
function attempt(fn) {
try {
fn();
} catch (e) {
//TODO: weird. buildExpectationResult is really a presenter for expectations
//so this should take an expectation object.
this.onStart(this);
this.queueRunner({
fns: allFns,
onException: function(e) {
self.addExpectationResult(false, self.expectationResultFactory({
matcherName: "",
passed: false,
@@ -54,35 +59,17 @@ jasmine.Spec.prototype.execute = function() {
message: self.exceptionFormatter(e),
trace: e
}));
if (!self.catchingExceptions()) {
//TODO: set a var when we catch an exception and
//use a finally block to close the loop in a nice way..
throw e;
}
}
}
},
onComplete: complete
});
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 complete() {
self.result.status = self.status();
self.resultCallback(self.result);
function resultCallback() {
self.resultCallback({
id: self.id,
status: self.status(),
description: self.description,
failedExpectations: self.failedExpectations
});
if (onComplete) {
onComplete();
}
}
};
@@ -98,7 +85,8 @@ jasmine.Spec.prototype.status = function() {
if (!this.encounteredExpectations) {
return null;
}
if (this.failedExpectations.length > 0) {
if (this.result.failedExpectations.length > 0) {
return 'failed';
} else {
return 'passed';

View File

@@ -3,33 +3,40 @@ jasmine.Suite = function(attrs) {
this.id = attrs.id;
this.parentSuite = attrs.parentSuite;
this.description = attrs.description;
this.onStart = attrs.onStart || function() {};
this.completeCallback = attrs.completeCallback || function() {};
this.resultCallback = attrs.resultCallback || function() {};
this.encourageGC = attrs.encourageGC || function(fn) {fn();};
this.beforeFns = [];
this.afterFns = [];
this.queueRunner = attrs.queueRunner || function() {};
this.disabled = false;
var queueFactory = attrs.queueFactory || function() {};
this.queue = queueFactory();
this.children_ = []; // TODO: rename
this.suites = []; // TODO: needed?
this.specs = []; // TODO: needed?
this.isSuite = attrs.isSuite || function() {};
this.children_ = []; // TODO: used by current reporters; keep for now
this.suites_ = [];
this.specs_ = [];
this.result = {
id: this.id,
status: this.disabled ? 'disabled' : '',
description: this.description,
fullName: this.getFullName()
};
};
jasmine.Suite.prototype.getFullName = function() {
var fullName = this.description;
for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {
fullName = parentSuite.description + ' ' + fullName;
if (parentSuite.parentSuite) {
fullName = parentSuite.description + ' ' + fullName;
}
}
return fullName;
};
jasmine.Suite.prototype.finish = function(onComplete) {
this.env.reporter.reportSuiteResults(this);
this.finished = true;
if (typeof(onComplete) == 'function') {
onComplete();
}
jasmine.Suite.prototype.disable = function() {
this.disabled = true;
};
jasmine.Suite.prototype.beforeEach = function(fn) {
@@ -40,32 +47,15 @@ jasmine.Suite.prototype.afterEach = function(fn) {
this.afterFns.unshift(fn);
};
//TODO: interface should be addSpec or addSuite methods.
jasmine.Suite.prototype.add = function(suiteOrSpec) {
this.children_.push(suiteOrSpec);
if (this.isSuite(suiteOrSpec)) {
this.suites_.push(suiteOrSpec);
this.env.currentRunner().addSuite(suiteOrSpec);
} else {
this.specs_.push(suiteOrSpec);
}
this.queue.add(suiteOrSpec);
jasmine.Suite.prototype.addSpec = function(spec) {
this.children_.push(spec);
this.specs.push(spec); // TODO: needed?
};
jasmine.Suite.prototype.specComplete = function(specResult) {
specResult.fullName = this.getFullName() + ' ' + specResult.description + '.';
specResult.suite = this;
this.env.removeAllSpies();
this.env.reporter.reportSpecResults(specResult);
this.queue.incrementQueue();
};
jasmine.Suite.prototype.specs = function() {
return this.specs_;
};
jasmine.Suite.prototype.suites = function() {
return this.suites_;
jasmine.Suite.prototype.addSuite = function(suite) {
suite.parentSuite = this;
this.children_.push(suite);
this.suites.push(suite); // TODO: needed?
};
jasmine.Suite.prototype.children = function() {
@@ -74,7 +64,36 @@ jasmine.Suite.prototype.children = function() {
jasmine.Suite.prototype.execute = function(onComplete) {
var self = this;
this.queue.start(function () {
self.finish(onComplete);
if (this.disabled) {
complete();
return;
}
var allFns = [],
children = this.children_;
for (var i = 0; i < children.length; i++) {
allFns.push(wrapChild(children[i]));
function wrapChild(child) {
return function(done) {
child.execute(done);
}
}
}
this.onStart(this);
this.queueRunner({
fns: allFns,
onComplete: complete
});
function complete() {
self.resultCallback(self.result);
if (onComplete) {
onComplete();
}
}
};