Rewrite Spec & allow Jasmine to be namespaced

- THere seems to be a performance regression. Large test suites may
  throw
- Regressions: Mock Clock won't install correctly, async specs are
  temporarily not supported.
- Async spec runs/waits interface is gone. Blocks are gone.
- Move most global usage into jasmine.Env constructor.
- Remove optional 'Jasmine running' from HtmlReporter -- caused
  NS_FACTORY_ERROR in firefox when tested
This commit is contained in:
Davis W. Frank & Rajan Agaskar
2012-12-05 09:37:05 -08:00
parent 779dee1211
commit a1011e7748
44 changed files with 1343 additions and 2586 deletions

View File

@@ -73,12 +73,22 @@ jasmine.ConsoleReporter = function(print, doneCallback, showColors) {
return newArr.join("\n");
}
function specFailureDetails(suiteDescription, specDescription, stackTraces) {
// function specFailureDetails(suiteDescription, specDescription, stackTraces) {
// newline();
// print(suiteDescription + " " + specDescription);
// newline();
// for (var i = 0; i < stackTraces.length; i++) {
// print(indent(stackTraces[i], 2));
// newline();
// }
// }
function specFailureDetails(specFailure) {
newline();
print(suiteDescription + " " + specDescription);
print(specFailure.fullName);
newline();
for (var i = 0; i < stackTraces.length; i++) {
print(indent(stackTraces[i], 2));
for (var i = 0; i < specFailure.failedExpectations.length; i++) {
var failedExpectation = specFailure.failedExpectations[i];
print(indent(failedExpectation.trace.stack, 2));
newline();
}
}
@@ -122,33 +132,48 @@ jasmine.ConsoleReporter = function(print, doneCallback, showColors) {
this.reportSpecStarting = function() { /* do nothing */
};
this.reportSpecResults = function(spec) {
var results = spec.results();
if (results.skipped) {
this.specFailures = [];
this.specCount = 0;
this.reportSpecResults = function(result) {
this.specCount++;
if (result.status === 'disabled') {
yellowStar();
} else if (results.passed()) {
} else if (result.status === 'passed') {
greenDot();
} else {
redF();
this.specFailures.push(result);
}
};
this.suiteResults = [];
// this.suiteResults = [];
this.reportSuiteResults = function(suite) {
var suiteResult = {
description: fullSuiteDescription(suite),
failedSpecResults: []
};
// var suiteResult = {
// description: fullSuiteDescription(suite),
// failedSpecResults: []
// };
suite.results().items_.forEach(function(spec) {
if (spec.failedCount > 0 && spec.description) suiteResult.failedSpecResults.push(spec);
});
// suite.results().items_.forEach(function(spec) {
// if (spec.failedCount > 0 && spec.description) suiteResult.failedSpecResults.push(spec);
// });
this.suiteResults.push(suiteResult);
// this.suiteResults.push(suiteResult);
};
function eachSpecFailure(suiteResults, callback) {
// function eachSpecFailure(suiteResults, callback) {
// for (var i = 0; i < suiteResults.length; i++) {
// var suiteResult = suiteResults[i];
// for (var j = 0; j < suiteResult.failedSpecResults.length; j++) {
// var failedSpecResult = suiteResult.failedSpecResults[j];
// var stackTraces = [];
// for (var k = 0; k < failedSpecResult.items_.length; k++) stackTraces.push(failedSpecResult.items_[k].trace.stack);
// callback(suiteResult.description, failedSpecResult.description, stackTraces);
// }
// }
// }
function eachSpecFailure(specResult, callback) {
for (var i = 0; i < suiteResults.length; i++) {
var suiteResult = suiteResults[i];
for (var j = 0; j < suiteResult.failedSpecResults.length; j++) {
@@ -163,15 +188,14 @@ jasmine.ConsoleReporter = function(print, doneCallback, showColors) {
this.reportRunnerResults = function(runner) {
newline();
eachSpecFailure(this.suiteResults, function(suiteDescription, specDescription, stackTraces) {
specFailureDetails(suiteDescription, specDescription, stackTraces);
});
for (var i = 0; i < this.specFailures.length; i++) {
specFailureDetails(this.specFailures[i]);
}
finished(this.now() - this.runnerStartTime);
var results = runner.results();
var summaryFunction = results.failedCount === 0 ? greenSummary : redSummary;
summaryFunction(runner.specs().length, results.failedCount);
doneCallback(runner);
var summaryFunction = this.specFailures.length === 0 ? greenSummary : redSummary;
summaryFunction(this.specCount, this.specFailures.length);
doneCallback(!!this.specFailures.length);
};
};

View File

@@ -1,27 +0,0 @@
/**
* Blocks are functions with executable code that make up a spec.
*
* @constructor
* @param {jasmine.Env} env
* @param {Function} func
* @param {jasmine.Spec} spec
*/
jasmine.Block = function(env, func, spec) {
this.env = env;
this.func = func;
this.spec = spec;
};
jasmine.Block.prototype.execute = function(onComplete) {
if (!jasmine.CATCH_EXCEPTIONS) {
this.func.apply(this.spec);
}
else {
try {
this.func.apply(this.spec);
} catch (e) {
this.spec.fail(e);
}
}
onComplete();
};

View File

@@ -4,15 +4,18 @@
* @constructor
*/
(function() {
function createSpec(env, suite, description) {
return new jasmine.Spec(env, suite, description);
}
jasmine.Env = function() {
var self = this;
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.currentSuite = null;
this.currentRunner_ = new jasmine.Runner(this);
this.catchExceptions = jasmine.CATCH_EXCEPTIONS;
this.undefined = jasmine.undefined;
this.reporter = new jasmine.MultiReporter();
@@ -34,6 +37,90 @@
jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);
var expectationFactory = function(actual, spec) {
var expect = new (self.matchersClass)(self, actual, spec);
expect.not = new (self.matchersClass)(self, actual, spec, true);
return expect;
};
var startCallback = function(spec) {
self.currentSpec = spec;
self.reporter.reportSpecStarting(spec);
};
var beforeFns = function(currentSuite) {
return function() {
var befores = [];
for (var suite = currentSuite; suite; suite = suite.parentSuite) {
befores = befores.concat(suite.before_)
}
return befores.concat(self.currentRunner_.before_).reverse();
}
};
var afterFns = function(currentSuite) {
return function() {
var afters = [];
for (var suite = currentSuite; suite; suite = suite.parentSuite) {
afters = afters.concat(suite.after_)
}
return afters.concat(self.currentRunner_.after_)
}
};
var exceptionFormatter = jasmine.util.formatException;
var specConstructor = jasmine.Spec;
// TODO: this deserves a better name (not a Factory the way others are)
var fullNameFactory = function(spec, currentSuite) {
var descriptions = [];
for (var suite = currentSuite; suite; suite = suite.parentSuite) {
descriptions.push(suite.description)
}
descriptions.push(spec.description);
return descriptions.join(' ') + '.';
};
var buildExpectationResult = jasmine.buildExpectationResult;
var expectationResultFactory = function(attrs) {
return buildExpectationResult(attrs);
};
this.specFactory = function(description, fn, suite) {
var spec = new specConstructor({
id: self.nextSpecId(),
beforeFns: beforeFns(suite),
afterFns: afterFns(suite),
expectationFactory: expectationFactory,
exceptionFormatter: exceptionFormatter,
//TODO: move spec creation to more appropriate level and remove this shim
resultCallback: function(result) { self.currentSpec = null; suite.specComplete(result); },
fullNameFactory: function(spec) { return fullNameFactory(spec, suite) },
startCallback: startCallback,
description: description,
catchExceptions: self.catchExceptions,
expectationResultFactory: expectationResultFactory,
fn: fn
});
if (!self.specFilter(spec)) {
spec.disable();
}
return spec;
};
var queueConstructor = jasmine.Queue;
var queueFactory = function() {
return new queueConstructor(self);
};
this.suiteFactory = function(description, specDefinitions) {
return new suiteConstructor(self, description, specDefinitions, self.currentSuite, queueFactory, isSuite);
};
};
@@ -42,22 +129,69 @@
jasmine.Env.prototype.setInterval = jasmine.setInterval;
jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
//TODO: shim Spec addMatchers behavior into Env. Should be rewritten to remove globals, etc.
jasmine.Env.prototype.addMatchers = function(matchersPrototype) {
var parent = this.matchersClass;
var newMatchersClass = function() {
parent.apply(this, arguments);
};
jasmine.util.inherit(newMatchersClass, parent);
jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
this.matchersClass = newMatchersClass;
};
/**
* @returns an object containing jasmine version build info, if set.
*/
jasmine.Env.prototype.version = function () {
if (jasmine.version_) {
return jasmine.version_;
if (this.jasmine.version_) {
return this.jasmine.version_;
} else {
throw new Error('Version not set');
}
};
jasmine.Env.prototype.expect = function(actual) {
return this.currentSpec.expect(actual);
};
jasmine.Env.prototype.spyOn = function(obj, methodName) {
if (obj == this.undefined) {
throw "spyOn could not find an object to spy upon for " + methodName + "()";
}
if (obj[methodName] === this.undefined) {
throw methodName + '() method does not exist';
}
if (obj[methodName] && obj[methodName].isSpy) {
//TODO?: should this return the current spy? Downside: may cause user confusion about spy state
throw new Error(methodName + ' has already been spied upon');
}
var spyObj = jasmine.createSpy(methodName);
this.spies_.push(spyObj);
spyObj.baseObj = obj;
spyObj.methodName = methodName;
spyObj.originalValue = obj[methodName];
obj[methodName] = spyObj;
return spyObj;
};
jasmine.Env.prototype.removeAllSpies = function() {
for (var i = 0; i < this.spies_.length; i++) {
var spy = this.spies_[i];
spy.baseObj[spy.methodName] = spy.originalValue;
}
this.spies_ = [];
};
/**
* @returns string containing jasmine version build info, if set.
*/
jasmine.Env.prototype.versionString = function() {
if (!jasmine.version_) {
if (!this.jasmine.version_) {
return "version unknown";
}
@@ -97,13 +231,13 @@
};
jasmine.Env.prototype.describe = function(description, specDefinitions) {
var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
var suite = this.suiteFactory(description, specDefinitions);
var parentSuite = this.currentSuite;
if (parentSuite) {
parentSuite.add(suite);
} else {
this.currentRunner_.add(suite);
this.currentRunner_.addSuite(suite);
}
this.currentSuite = suite;
@@ -154,15 +288,9 @@
};
};
jasmine.Env.prototype.it = function(description, func) {
var spec = createSpec(this, this.currentSuite, description);
jasmine.Env.prototype.it = function(description, fn) {
var spec = this.specFactory(description, fn, this.currentSuite);
this.currentSuite.add(spec);
this.currentSpec = spec;
if (func) {
spec.runs(func);
}
return spec;
};
@@ -202,7 +330,7 @@
b.__Jasmine_been_here_before__ = a;
var hasKey = function(obj, keyName) {
return obj !== null && obj[keyName] !== jasmine.undefined;
return obj !== null && obj[keyName] !== this.undefined;
};
for (var property in b) {
@@ -238,13 +366,13 @@
for (var i = 0; i < this.equalityTesters_.length; i++) {
var equalityTester = this.equalityTesters_[i];
var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
if (result !== jasmine.undefined) return result;
if (result !== this.undefined) return result;
}
if (a === b) return true;
if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
return (a == jasmine.undefined && b == jasmine.undefined);
if (a === this.undefined || a === null || b === this.undefined || b === null) {
return (a == this.undefined && b == this.undefined);
}
if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {

View File

@@ -2,7 +2,8 @@
*
* @constructor
*/
jasmine.JsApiReporter = function() {
jasmine.JsApiReporter = function(jasmine) {
this.jasmine = jasmine || {};
this.started = false;
this.finished = false;
this.suites_ = [];
@@ -23,7 +24,7 @@ jasmine.JsApiReporter.prototype.suites = function() {
};
jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {
var isSuite = suiteOrSpec instanceof jasmine.Suite;
var isSuite = suiteOrSpec instanceof this.jasmine.Suite;
var summary = {
id: suiteOrSpec.id,
name: suiteOrSpec.description,
@@ -44,10 +45,6 @@ jasmine.JsApiReporter.prototype.results = function() {
return this.results_;
};
jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {
return this.results_[specId];
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {
this.finished = true;
@@ -58,10 +55,11 @@ jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {
};
//noinspection JSUnusedLocalSymbols
jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
this.results_[spec.id] = {
messages: spec.results().getItems(),
result: spec.results().failedCount > 0 ? "failed" : "passed"
jasmine.JsApiReporter.prototype.reportSpecResults = function(result) {
this.results_[result.id] = {
messages: result.failedExpectations,
//result is status
result: result.status
};
};
@@ -69,6 +67,7 @@ jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {
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++) {
@@ -83,14 +82,16 @@ jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){
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({
text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,
//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,
passed: resultMessage.passed || true, //status === 'passed'
type: resultMessage.type,
message: resultMessage.message,
trace: {
stack: !resultMessage.passed ? resultMessage.trace.stack : jasmine.undefined
stack: !resultMessage.passed ? resultMessage.trace.stack : this.jasmine.undefined
}
});
}

View File

@@ -5,6 +5,7 @@
* @param {jasmine.Spec} spec
*/
jasmine.Matchers = function(env, actual, spec, opt_isNot) {
//TODO: true dependency: equals, contains
this.env = env;
this.actual = actual;
this.spec = spec;
@@ -16,6 +17,7 @@ jasmine.Matchers.pp = function(str) {
throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
};
jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {
for (var methodName in prototype) {
var orig = prototype[methodName];
@@ -58,7 +60,7 @@ jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {
actual: this.actual,
message: message
});
this.spec.addMatcherResult(expectationResult);
this.spec.addExpectationResult(result, expectationResult);
return jasmine.undefined;
};
};

View File

@@ -12,7 +12,7 @@ jasmine.Queue = function(env) {
};
jasmine.Queue.prototype.addBefore = function(block, ensure) {
if (ensure === jasmine.undefined) {
if (ensure === this.env.undefined) {
ensure = false;
}
@@ -21,7 +21,7 @@ jasmine.Queue.prototype.addBefore = function(block, ensure) {
};
jasmine.Queue.prototype.add = function(block, ensure) {
if (ensure === jasmine.undefined) {
if (ensure === this.env.undefined) {
ensure = false;
}
@@ -30,7 +30,7 @@ jasmine.Queue.prototype.add = function(block, ensure) {
};
jasmine.Queue.prototype.insertNext = function(block, ensure) {
if (ensure === jasmine.undefined) {
if (ensure === this.env.undefined) {
ensure = false;
}
@@ -51,50 +51,55 @@ jasmine.Queue.prototype.isRunning = function() {
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;
// var goAgain = true;
while (goAgain) {
goAgain = false;
// while (goAgain) {
// goAgain = false;
if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {
var calledSynchronously = true;
var completedSynchronously = false;
// var calledSynchronously = true;
// var completedSynchronously = false;
var onComplete = function () {
if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
completedSynchronously = true;
return;
}
// var onComplete = function () {
// if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {
// completedSynchronously = true;
// return;
// }
if (self.blocks[self.index].abort) {
self.abort = true;
}
self.blocks[self.index].execute(function() { self.incrementQueue() });
self.offset = 0;
self.index++;
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(onComplete);
// 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();
}
// calledSynchronously = false;
// if (completedSynchronously) {
// onComplete();
// }
} else {
self.running = false;
@@ -102,5 +107,5 @@ jasmine.Queue.prototype.next_ = function() {
self.onComplete();
}
}
}
// }
};

View File

@@ -1,16 +1,18 @@
//TODO: runner is a special case of suite.
/**
* Runner
*
* @constructor
* @param {jasmine.Env} env
*/
jasmine.Runner = function(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() {
@@ -40,13 +42,18 @@ jasmine.Runner.prototype.finishCallback = function() {
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 (block instanceof jasmine.Suite) {
if (this.isSuite(block)) {
this.addSuite(block);
} else {
this.queue.add(block);
}
this.queue.add(block);
};
jasmine.Runner.prototype.specs = function () {

View File

@@ -1,243 +1,97 @@
/**
* Internal representation of a Jasmine specification, or test.
*
* @constructor
* @param {jasmine.Env} env
* @param {jasmine.Suite} suite
* @param {String} description
*/
jasmine.Spec = function(env, suite, description) {
if (!env) {
throw new Error('jasmine.Env() required');
jasmine.Spec = function(attrs) {
this.failedExpectations = [];
this.encounteredExpectations = false;
this.expectationFactory = attrs.expectationFactory;
this.resultCallback = attrs.resultCallback || function() {};
this.id = attrs.id;
this.description = attrs.description;
this.fn = attrs.fn;
this.beforeFns = attrs.beforeFns || function() {};
this.afterFns = attrs.afterFns || function() {};
this.catchExceptions = attrs.catchExceptions;
this.startCallback = attrs.startCallback || function() {};
this.exceptionFormatter = attrs.exceptionFormatter || function() {};
this.fullNameFactory = attrs.fullNameFactory;
this.expectationResultFactory = attrs.expectationResultFactory || function() {};
};
jasmine.Spec.prototype.addExpectationResult = function(passed, data) {
this.encounteredExpectations = true;
if (!passed) {
this.failedExpectations.push(data);
}
if (!suite) {
throw new Error('jasmine.Suite() required');
}
var spec = this;
spec.id = env.nextSpecId ? env.nextSpecId() : null;
spec.env = env;
spec.suite = suite;
spec.description = description;
spec.queue = new jasmine.Queue(env);
spec.afterCallbacks = [];
spec.spies_ = [];
spec.results_ = new jasmine.NestedResults();
spec.results_.description = description;
spec.matchersClass = null;
};
jasmine.Spec.prototype.getFullName = function() {
return this.suite.getFullName() + ' ' + this.description + '.';
};
jasmine.Spec.prototype.results = function() {
return this.results_;
};
/**
* All parameters are pretty-printed and concatenated together, then written to the spec's output.
*
* Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/
jasmine.Spec.prototype.log = function() {
return this.results_.log(arguments);
};
jasmine.Spec.prototype.runs = function (func) {
var block = new jasmine.Block(this.env, func, this);
this.addToQueue(block);
return this;
};
jasmine.Spec.prototype.addToQueue = function (block) {
if (this.queue.isRunning()) {
this.queue.insertNext(block);
} else {
this.queue.add(block);
}
};
/**
* @param {jasmine.ExpectationResult} result
*/
jasmine.Spec.prototype.addMatcherResult = function(result) {
this.results_.addResult(result);
};
jasmine.Spec.prototype.expect = function(actual) {
var positive = new (this.getMatchersClass_())(this.env, actual, this);
positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
return positive;
return this.expectationFactory(actual, this);
};
/**
* Waits a fixed time period before moving to the next block.
*
* @deprecated Use waitsFor() instead
* @param {Number} timeout milliseconds to wait
*/
jasmine.Spec.prototype.waits = function(timeout) {
var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
this.addToQueue(waitsFunc);
return this;
};
/**
* Waits for the latchFunction to return true before proceeding to the next block.
*
* @param {Function} latchFunction
* @param {String} optional_timeoutMessage
* @param {Number} optional_timeout
*/
jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {
var latchFunction_ = null;
var optional_timeoutMessage_ = null;
var optional_timeout_ = null;
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
switch (typeof arg) {
case 'function':
latchFunction_ = arg;
break;
case 'string':
optional_timeoutMessage_ = arg;
break;
case 'number':
optional_timeout_ = arg;
break;
}
}
var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);
this.addToQueue(waitsForFunc);
return this;
};
jasmine.Spec.prototype.fail = function (e) {
var expectationResult = jasmine.buildExpectationResult({
passed: false,
message: e ? jasmine.util.formatException(e) : 'Exception',
trace: { stack: e.stack }
});
this.results_.addResult(expectationResult);
};
jasmine.Spec.prototype.getMatchersClass_ = function() {
return this.matchersClass || this.env.matchersClass;
};
jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
var parent = this.getMatchersClass_();
var newMatchersClass = function() {
parent.apply(this, arguments);
};
jasmine.util.inherit(newMatchersClass, parent);
jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
this.matchersClass = newMatchersClass;
};
jasmine.Spec.prototype.finishCallback = function() {
this.env.reporter.reportSpecResults(this);
};
jasmine.Spec.prototype.finish = function(onComplete) {
this.removeAllSpies();
this.finishCallback();
if (onComplete) {
onComplete();
}
};
jasmine.Spec.prototype.after = function(doAfter) {
if (this.queue.isRunning()) {
this.queue.add(new jasmine.Block(this.env, doAfter, this), true);
} else {
this.afterCallbacks.unshift(doAfter);
}
};
jasmine.Spec.prototype.execute = function(onComplete) {
var spec = this;
if (!spec.env.specFilter(spec)) {
spec.results_.skipped = true;
spec.finish(onComplete);
jasmine.Spec.prototype.execute = function() {
if (this.disabled) {
resultCallback.call(this);
return;
}
this.env.reporter.reportSpecStarting(this);
spec.env.currentSpec = spec;
spec.addBeforesAndAftersToQueue();
spec.queue.start(function () {
spec.finish(onComplete);
});
};
jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
var runner = this.env.currentRunner();
var i;
for (var suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
var befores = this.beforeFns() || [],
afters = this.afterFns() || [];
this.startCallback(this);
try {
for (var i = 0; i < befores.length; i++) {
befores[i].call(this);
}
this.fn.call(this);
for (i = 0; i < afters.length; i++) {
afters[i].call(this);
}
} catch (e) {
//TODO: weird. buildExpectationResult is really a presenter for expectations
//so this should take an expectation object.
this.addExpectationResult(false, this.expectationResultFactory({
matcherName: "",
passed: false,
expected: "",
actual: "",
message: this.exceptionFormatter(e),
trace: e
}));
if (!this.catchExceptions) {
throw e;
}
}
for (i = 0; i < runner.before_.length; i++) {
this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
finally {
resultCallback.call(this);
}
for (i = 0; i < this.afterCallbacks.length; i++) {
this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);
}
for (suite = this.suite; suite; suite = suite.parentSuite) {
for (i = 0; i < suite.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);
}
}
for (i = 0; i < runner.after_.length; i++) {
this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);
function resultCallback() {
this.resultCallback({
id: this.id,
status: this.status(),
description: this.description,
failedExpectations: this.failedExpectations
});
}
};
jasmine.Spec.prototype.explodes = function() {
throw 'explodes function should not have been called';
jasmine.Spec.prototype.disable = function() {
this.disabled = true;
};
jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
if (obj == jasmine.undefined) {
throw "spyOn could not find an object to spy upon for " + methodName + "()";
jasmine.Spec.prototype.status = function() {
if (this.disabled) {
return 'disabled';
}
if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
throw methodName + '() method does not exist';
if (!this.encounteredExpectations) {
return null;
}
if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
throw new Error(methodName + ' has already been spied upon');
if (this.failedExpectations.length > 0) {
return 'failed';
} else {
return 'passed';
}
var spyObj = jasmine.createSpy(methodName);
this.spies_.push(spyObj);
spyObj.baseObj = obj;
spyObj.methodName = methodName;
spyObj.originalValue = obj[methodName];
obj[methodName] = spyObj;
return spyObj;
};
jasmine.Spec.prototype.removeAllSpies = function() {
for (var i = 0; i < this.spies_.length; i++) {
var spy = this.spies_[i];
spy.baseObj[spy.methodName] = spy.originalValue;
}
this.spies_ = [];
};
//TODO: remove
jasmine.Spec.prototype.getFullName = function() {
return this.fullNameFactory(this);
}

View File

@@ -7,13 +7,16 @@
* @param {Function} specDefinitions
* @param {jasmine.Suite} parentSuite
*/
jasmine.Suite = function(env, description, specDefinitions, parentSuite) {
jasmine.Suite = function(env, description, specDefinitions, parentSuite, queueFactory, isSuite) {
var self = this;
//TODO: remove once we unit test Suite
var queueFactory = queueFactory || function() {};
self.id = env.nextSuiteId ? env.nextSuiteId() : null;
self.description = description;
self.queue = new jasmine.Queue(env);
self.queue = queueFactory();
self.parentSuite = parentSuite;
self.env = env;
self.isSuite = isSuite || function() {};
self.before_ = [];
self.after_ = [];
self.children_ = [];
@@ -47,9 +50,10 @@ jasmine.Suite.prototype.afterEach = function(afterEachFunction) {
this.after_.unshift(afterEachFunction);
};
//TODO: interface should be addSpec or addSuite methods.
jasmine.Suite.prototype.add = function(suiteOrSpec) {
this.children_.push(suiteOrSpec);
if (suiteOrSpec instanceof jasmine.Suite) {
if (this.isSuite(suiteOrSpec)) {
this.suites_.push(suiteOrSpec);
this.env.currentRunner().addSuite(suiteOrSpec);
} else {
@@ -58,6 +62,14 @@ jasmine.Suite.prototype.add = function(suiteOrSpec) {
this.queue.add(suiteOrSpec);
};
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_;
};

View File

@@ -1,15 +0,0 @@
jasmine.WaitsBlock = function(env, timeout, spec) {
this.timeout = timeout;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);
jasmine.WaitsBlock.prototype.execute = function (onComplete) {
if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');
}
this.env.setTimeout(function () {
onComplete();
}, this.timeout);
};

View File

@@ -1,54 +0,0 @@
/**
* A block which waits for some condition to become true, with timeout.
*
* @constructor
* @extends jasmine.Block
* @param {jasmine.Env} env The Jasmine environment.
* @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
* @param {Function} latchFunction A function which returns true when the desired condition has been met.
* @param {String} message The message to display if the desired condition hasn't been met within the given time period.
* @param {jasmine.Spec} spec The Jasmine spec.
*/
jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {
this.timeout = timeout || env.defaultTimeoutInterval;
this.latchFunction = latchFunction;
this.message = message;
this.totalTimeSpentWaitingForLatch = 0;
jasmine.Block.call(this, env, null, spec);
};
jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
if (jasmine.VERBOSE) {
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
}
var latchFunctionResult;
try {
latchFunctionResult = this.latchFunction.apply(this.spec);
} catch (e) {
this.spec.fail(e);
onComplete();
return;
}
if (latchFunctionResult) {
onComplete();
} else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {
var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');
this.spec.fail({
name: 'timeout',
message: message
});
this.abort = true;
onComplete();
} else {
this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;
var self = this;
this.env.setTimeout(function() {
self.execute(onComplete);
}, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);
}
};

View File

@@ -5,6 +5,9 @@
* @namespace
*/
var jasmine = {};
if (typeof window == "undefined" && typeof exports == "object") {
exports.jasmine = jasmine
}
/**
* @private
@@ -431,13 +434,3 @@ jasmine.createSpyObj = function(baseName, methodNames) {
}
return obj;
};
/**
* All parameters are pretty-printed and concatenated together, then written to the current spec's output.
*
* Be careful not to leave calls to <code>jasmine.log</code> in production code.
*/
jasmine.log = function() {
var spec = jasmine.getEnv().currentSpec;
spec.log.apply(spec, arguments);
};

View File

@@ -111,6 +111,7 @@ jasmine.Clock = {
useMock: function() {
if (!jasmine.Clock.isInstalled()) {
//TODO: this is using an interface that doesn't exist.
var spec = jasmine.getEnv().currentSpec;
spec.after(jasmine.Clock.uninstallMock);

View File

@@ -1,5 +1,6 @@
jasmine.HtmlReporter = function(_doc) {
jasmine.HtmlReporter = function(_doc, jasmine) {
var self = this;
this.jasmine = jasmine || window.jasmine;
var doc = _doc || window.document;
var reporterView;
@@ -7,7 +8,6 @@ jasmine.HtmlReporter = function(_doc) {
var dom = {};
// Jasmine Reporter Public Interface
self.logRunningSpecs = false;
self.reportRunnerStarting = function(runner) {
var specs = runner.specs() || [];
@@ -20,7 +20,7 @@ jasmine.HtmlReporter = function(_doc) {
doc.body.appendChild(dom.reporter);
setExceptionHandling();
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
reporterView = new self.jasmine.HtmlReporter.ReporterView(dom, self.jasmine);
reporterView.addSpecs(specs, self.specFilter);
};
@@ -33,17 +33,14 @@ jasmine.HtmlReporter = function(_doc) {
};
self.reportSpecStarting = function(spec) {
if (self.logRunningSpecs) {
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
}
};
self.reportSpecResults = function(spec) {
reporterView.specComplete(spec);
self.reportSpecResults = function(result) {
reporterView.specComplete(result);
};
self.log = function() {
var console = jasmine.getGlobal().console;
var console = self.jasmine.getGlobal().console;
if (console && console.log) {
if (console.log.apply) {
console.log.apply(console, arguments);
@@ -72,7 +69,7 @@ jasmine.HtmlReporter = function(_doc) {
}
var paramMap = [];
var params = jasmine.HtmlReporter.parameters(doc);
var params = self.jasmine.HtmlReporter.parameters(doc);
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
@@ -118,7 +115,7 @@ jasmine.HtmlReporter = function(_doc) {
}
i++;
}
if (jasmine.CATCH_EXCEPTIONS) {
if (self.jasmine.CATCH_EXCEPTIONS) {
params.push("catch=false");
}
@@ -130,7 +127,7 @@ jasmine.HtmlReporter = function(_doc) {
if (noTryCatch()) {
chxCatch.setAttribute('checked', true);
jasmine.CATCH_EXCEPTIONS = false;
self.jasmine.CATCH_EXCEPTIONS = false;
}
chxCatch.onclick = function() {
window.location.search = searchWithCatch();
@@ -146,14 +143,14 @@ jasmine.HtmlReporter.parameters = function(doc) {
}
return params;
}
jasmine.HtmlReporter.sectionLink = function(sectionName) {
jasmine.HtmlReporter.sectionLink = function(sectionName, catchExceptions) {
var link = '?';
var params = [];
if (sectionName) {
params.push('spec=' + encodeURIComponent(sectionName));
}
if (!jasmine.CATCH_EXCEPTIONS) {
if (!catchExceptions) {
params.push("catch=false");
}
if (params.length > 0) {

View File

@@ -46,7 +46,7 @@ jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
if (parent) {
if (typeof this.views.suites[parent.id] == 'undefined') {
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
this.views.suites[parent.id] = new this.jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views, this.jasmine);
}
parentDiv = this.views.suites[parent.id].element;
}
@@ -56,6 +56,7 @@ jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
//TODO: not really a helper, thus, no this.jasmine
for(var fn in jasmine.HtmlReporterHelpers) {
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
}

View File

@@ -1,10 +1,11 @@
jasmine.HtmlReporter.ReporterView = function(dom) {
jasmine.HtmlReporter.ReporterView = function(dom, jasmine) {
this.startedAt = new Date();
this.runningSpecCount = 0;
this.completeSpecCount = 0;
this.passedCount = 0;
this.failedCount = 0;
this.skippedCount = 0;
this.jasmine = jasmine || {};
this.createResultsMenu = function() {
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
@@ -31,21 +32,21 @@ jasmine.HtmlReporter.ReporterView = function(dom) {
for (var i = 0; i < specs.length; i++) {
var spec = specs[i];
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
this.views.specs[spec.id] = new this.jasmine.HtmlReporter.SpecView(spec, dom, this.views, this.jasmine);
if (specFilter(spec)) {
this.runningSpecCount++;
}
}
};
this.specComplete = function(spec) {
this.specComplete = function(result) {
this.completeSpecCount++;
if (isUndefined(this.views.specs[spec.id])) {
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
}
// if (isUndefined(this.views.specs[result.id])) {
// this.views.specs[result.id] = new this.jasmine.HtmlReporter.SpecView(result, dom);
// }
var specView = this.views.specs[spec.id];
var specView = this.views.specs[result.id];
switch (specView.status()) {
case 'passed':
@@ -56,7 +57,7 @@ jasmine.HtmlReporter.ReporterView = function(dom) {
this.failedCount++;
break;
case 'skipped':
case 'disabled':
this.skippedCount++;
break;
}
@@ -81,14 +82,14 @@ jasmine.HtmlReporter.ReporterView = function(dom) {
// currently running UI
if (isUndefined(this.runningAlert)) {
this.runningAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "runningAlert bar" });
this.runningAlert = this.createDom('a', { href: this.jasmine.HtmlReporter.sectionLink(null, this.jasmine.CATCH_EXCEPTIONS), className: "runningAlert bar" });
dom.alert.appendChild(this.runningAlert);
}
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
// skipped specs UI
if (isUndefined(this.skippedAlert)) {
this.skippedAlert = this.createDom('a', { href: jasmine.HtmlReporter.sectionLink(), className: "skippedAlert bar" });
this.skippedAlert = this.createDom('a', { href: this.jasmine.HtmlReporter.sectionLink(null, this.jasmine.CATCH_EXCEPTIONS), className: "skippedAlert bar" });
}
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
@@ -99,7 +100,7 @@ jasmine.HtmlReporter.ReporterView = function(dom) {
// passing specs UI
if (isUndefined(this.passedAlert)) {
this.passedAlert = this.createDom('span', { href: jasmine.HtmlReporter.sectionLink(), className: "passingAlert bar" });
this.passedAlert = this.createDom('span', { href: this.jasmine.HtmlReporter.sectionLink(null, this.jasmine.CATCH_EXCEPTIONS), className: "passingAlert bar" });
}
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);

View File

@@ -1,7 +1,8 @@
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
jasmine.HtmlReporter.SpecView = function(spec, dom, views, jasmine) {
this.spec = spec;
this.dom = dom;
this.views = views;
this.jasmine = jasmine || {};
this.symbol = this.createDom('li', { className: 'pending' });
this.dom.symbolSummary.appendChild(this.symbol);
@@ -9,7 +10,7 @@ jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
this.summary = this.createDom('div', { className: 'specSummary' },
this.createDom('a', {
className: 'description',
href: jasmine.HtmlReporter.sectionLink(this.spec.getFullName()),
href: this.jasmine.HtmlReporter.sectionLink(this.spec.getFullName(), this.jasmine.CATCH_EXCEPTIONS),
title: this.spec.getFullName()
}, this.spec.description)
);
@@ -24,16 +25,15 @@ jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
};
jasmine.HtmlReporter.SpecView.prototype.status = function() {
return this.getSpecStatus(this.spec);
return this.spec.status();
};
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
this.symbol.className = this.status();
switch (this.status()) {
case 'skipped':
case 'disabled':
break;
case 'passed':
this.appendSummaryToSuiteDiv();
break;
@@ -53,7 +53,7 @@ jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
this.detail.className += ' ' + this.status();
var resultItems = this.spec.results().getItems();
var resultItems = this.spec.failedExpectations;
var messagesDiv = this.createDom('div', { className: 'messages' });
for (var i = 0; i < resultItems.length; i++) {

View File

@@ -1,10 +1,11 @@
jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
jasmine.HtmlReporter.SuiteView = function(suite, dom, views, jasmine) {
this.suite = suite;
this.dom = dom;
this.views = views;
this.jasmine = jasmine || {};
this.element = this.createDom('div', { className: 'suite' },
this.createDom('a', { className: 'description', href: jasmine.HtmlReporter.sectionLink(this.suite.getFullName()) }, this.suite.description)
this.createDom('a', { className: 'description', href: this.jasmine.HtmlReporter.sectionLink(this.suite.getFullName(), this.jasmine.CATCH_EXCEPTIONS) }, this.suite.description)
);
this.appendToSummary(this.suite, this.element);