From a1a278ee6973b30d4c6392a655906e46090b3695 Mon Sep 17 00:00:00 2001 From: ragaskar Date: Wed, 29 Jul 2009 22:27:11 -0700 Subject: [PATCH] Major refactoring of Spec. Moved QueuedFunction to Block, WaitsBlock and WaitsForBlock. Waits and WaitsFor blocks now sequentially stackable --- Rakefile | 4 +- lib/jasmine.js | 1224 ++++++++++++++++---------------- spec/bootstrap.html | 4 +- spec/runner.html | 6 +- spec/suites/SpecRunningTest.js | 210 +++++- src/Block.js | 31 + src/QueuedFunction.js | 84 --- src/Spec.js | 51 +- src/WaitsBlock.js | 14 + src/WaitsForBlock.js | 38 + 10 files changed, 912 insertions(+), 754 deletions(-) create mode 100644 src/Block.js delete mode 100644 src/QueuedFunction.js create mode 100644 src/WaitsBlock.js create mode 100644 src/WaitsForBlock.js diff --git a/Rakefile b/Rakefile index 64d7cdbc..fcd5025d 100644 --- a/Rakefile +++ b/Rakefile @@ -1,8 +1,8 @@ desc 'Builds lib/jasmine from source' task :build do - # these files must be better - sources = ["src/base.js", "src/util.js", "src/Env.js", "src/ActionCollection.js", "src/Reporter.js"] + # these files must be loaded first + sources = ["src/base.js", "src/util.js", "src/Env.js", "src/ActionCollection.js", "src/Reporter.js", "src/Block.js"] sources += Dir.glob('src/*.js').reject{|f| sources.include?(f)} diff --git a/lib/jasmine.js b/lib/jasmine.js index c37406cd..851d6068 100644 --- a/lib/jasmine.js +++ b/lib/jasmine.js @@ -872,6 +872,597 @@ jasmine.Reporter.prototype.reportSpecResults = function(spec) { jasmine.Reporter.prototype.log = function (str) { }; +/** + * 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.next = function() { + this.spec.finish(); // default value is to be done after one function +}; + +jasmine.Block.prototype.execute = function() { + this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...'); + try { + this.func.apply(this.spec); + } catch (e) { + this.fail(e); + } + this.next(); +}; + +jasmine.Block.prototype.fail = function(e) { + this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); +}; +/** + * Internal representation of a Jasmine suite. + * + * @constructor + * @param {jasmine.Env} env + * @param {String} description + * @param {Function} specDefinitions + * @param {jasmine.Suite} parentSuite + */ +jasmine.Suite = function(env, description, specDefinitions, parentSuite) { + jasmine.ActionCollection.call(this, env); + + this.description = description; + this.specs = this.actions; + this.parentSuite = parentSuite; + + this.beforeEachFunction = null; + this.afterEachFunction = null; +}; +jasmine.util.inherit(jasmine.Suite, jasmine.ActionCollection); + +jasmine.Suite.prototype.getFullName = function() { + var fullName = this.description; + for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { + fullName = parentSuite.description + ' ' + fullName; + } + return fullName; +}; + +jasmine.Suite.prototype.finishCallback = function() { + this.env.reporter.reportSuiteResults(this); +}; + +jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { + beforeEachFunction.typeName = 'beforeEach'; + this.beforeEachFunction = beforeEachFunction; +}; + +jasmine.Suite.prototype.afterEach = function(afterEachFunction) { + afterEachFunction.typeName = 'afterEach'; + this.afterEachFunction = afterEachFunction; +}; + +jasmine.Suite.prototype.getResults = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.specs.length; i++) { + results.rollupCounts(this.specs[i].getResults()); + } + return results; +}; + +/** + * Base class for pretty printing for expectation results. + */ +jasmine.PrettyPrinter = function() { + this.ppNestLevel_ = 0; +}; + +/** + * Formats a value in a nice, human-readable string. + * + * @param value + * @returns {String} + */ +jasmine.PrettyPrinter.prototype.format = function(value) { + if (this.ppNestLevel_ > 40) { + // return '(jasmine.pp nested too deeply!)'; + throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); + } + + this.ppNestLevel_++; + try { + if (value === undefined) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value.navigator && value.frames && value.setTimeout) { + this.emitScalar(''); + } else if (value instanceof jasmine.Matchers.Any) { + this.emitScalar(value.toString()); + } else if (typeof value === 'string') { + this.emitScalar("'" + value + "'"); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (typeof value.nodeType === 'number') { + this.emitScalar('HTMLNode'); + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (value.__Jasmine_been_here_before__) { + this.emitScalar(''); + } else if (jasmine.isArray_(value) || typeof value == 'object') { + value.__Jasmine_been_here_before__ = true; + if (jasmine.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + delete value.__Jasmine_been_here_before__; + } else { + this.emitScalar(value.toString()); + } + } finally { + this.ppNestLevel_--; + } +}; + +jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { + for (var property in obj) { + if (property == '__Jasmine_been_here_before__') continue; + fn(property, obj.__lookupGetter__(property) != null); + } +}; + +jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; +jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; + +jasmine.StringPrettyPrinter = function() { + jasmine.PrettyPrinter.call(this); + + this.string = ''; +}; +jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); + +jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { + this.append(value); +}; + +jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { + this.append('[ '); + for (var i = 0; i < array.length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + this.append(' ]'); +}; + +jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { + var self = this; + this.append('{ '); + var first = true; + + this.iterateObject(obj, function(property, isGetter) { + if (first) { + first = false; + } else { + self.append(', '); + } + + self.append(property); + self.append(' : '); + if (isGetter) { + self.append(''); + } else { + self.format(obj[property]); + } + }); + + this.append(' }'); +}; + +jasmine.StringPrettyPrinter.prototype.append = function(value) { + this.string += value; +}; +/** + * @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", "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); + } +})(); +/** + * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults + * + * @constructor + */ +jasmine.NestedResults = function() { + /** + * The total count of results + */ + this.totalCount = 0; + /** + * Number of passed results + */ + this.passedCount = 0; + /** + * Number of failed results + */ + this.failedCount = 0; + /** + * Was this suite/spec skipped? + */ + this.skipped = false; + /** + * @ignore + */ + this.items_ = []; +}; + +/** + * Roll up the result counts. + * + * @param result + */ +jasmine.NestedResults.prototype.rollupCounts = function(result) { + this.totalCount += result.totalCount; + this.passedCount += result.passedCount; + this.failedCount += result.failedCount; +}; + +/** + * Tracks a result's message. + * @param message + */ +jasmine.NestedResults.prototype.log = function(message) { + this.items_.push(new jasmine.MessageResult(message)); +}; + +/** + * Getter for the results: message & results. + */ +jasmine.NestedResults.prototype.getItems = function() { + return this.items_; +}; + +/** + * Adds a result, tracking counts (total, passed, & failed) + * @param {jasmine.ExpectationResult|jasmine.NestedResults} result + */ +jasmine.NestedResults.prototype.addResult = function(result) { + if (result.type != 'MessageResult') { + if (result.items_) { + this.rollupCounts(result); + } else { + this.totalCount++; + if (result.passed) { + this.passedCount++; + } else { + this.failedCount++; + } + } + } + this.items_.push(result); +}; + +/** + * @returns {Boolean} True if everything below passed + */ +jasmine.NestedResults.prototype.passed = function() { + return this.passedCount === this.totalCount; +}; +/** + * Runner + * + * @constructor + * @param {jasmine.Env} env + */ +jasmine.Runner = function(env) { + jasmine.ActionCollection.call(this, env); + + this.suites = this.actions; +}; +jasmine.util.inherit(jasmine.Runner, jasmine.ActionCollection); + +jasmine.Runner.prototype.execute = function() { + if (this.env.reporter.reportRunnerStarting) { + this.env.reporter.reportRunnerStarting(this); + } + jasmine.ActionCollection.prototype.execute.call(this); +}; + +jasmine.Runner.prototype.finishCallback = function() { + this.env.reporter.reportRunnerResults(this); +}; + +jasmine.Runner.prototype.getResults = function() { + var results = new jasmine.NestedResults(); + for (var i = 0; i < this.suites.length; i++) { + results.rollupCounts(this.suites[i].getResults()); + } + return results; +}; +/* JasmineReporters.reporter + * Base object that will get called whenever a Spec, Suite, or Runner is done. It is up to + * descendants of this object to do something with the results (see json_reporter.js) + */ +jasmine.Reporters = {}; + +jasmine.Reporters.reporter = function(callbacks) { + var that = { + callbacks: callbacks || {}, + + doCallback: function(callback, results) { + if (callback) { + callback(results); + } + }, + + reportRunnerResults: function(runner) { + that.doCallback(that.callbacks.runnerCallback, runner); + }, + reportSuiteResults: function(suite) { + that.doCallback(that.callbacks.suiteCallback, suite); + }, + reportSpecResults: function(spec) { + that.doCallback(that.callbacks.specCallback, spec); + }, + log: function (str) { + if (console && console.log) console.log(str); + } + }; + + return that; +}; + +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 () { + var self = this; + self.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + self.env.setTimeout(function () { + self.next(); + }, self.timeout); +}; +/** + * 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) { + this.id = env.nextSpecId_++; + this.env = env; + this.suite = suite; + this.description = description; + this.queue = []; + this.finished = false; + this.afterCallbacks = []; + this.spies_ = []; + + this.results = new jasmine.NestedResults(); + this.results.description = description; + this.runs = this.addToQueue; + this.matchersClass = null; +}; + +jasmine.Spec.prototype.getFullName = function() { + return this.suite.getFullName() + ' ' + this.description + '.'; +}; + +jasmine.Spec.prototype.getResults = function() { + return this.results; +}; + +jasmine.Spec.prototype.addToQueue = function(func) { + var block = new jasmine.Block(this.env, func, this); + + this.setNextOnLastInQueue(block); + + this.queue.push(block); + + return this; +}; + +/** + * @private + * @deprecated + */ +jasmine.Spec.prototype.expects_that = function(actual) { + return this.expect(actual); +}; + +/** +* @private + */ +jasmine.Spec.prototype.expect = function(actual) { + return new (this.getMatchersClass_())(this.env, actual, this.results); +}; + +/** + * @private + */ +jasmine.Spec.prototype.setNextOnLastInQueue = function (block) { + if (this.queue.length > 0) { + var previousBlock = this.queue[this.queue.length - 1]; + previousBlock.next = function() { + block.execute(); + }; + } +}; + +/** + * @private + */ +jasmine.Spec.prototype.waits = function(timeout) { + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.setNextOnLastInQueue(waitsFunc); + this.queue.push(waitsFunc); + return this; +}; + +/** + * @private + */ +jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) { + var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this); + this.setNextOnLastInQueue(waitsForFunc); + this.queue.push(waitsForFunc); + return this; +}; + +jasmine.Spec.prototype.failWithException = function (e) { + this.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); +}; + +jasmine.Spec.prototype.getMatchersClass_ = function() { + return this.matchersClass || jasmine.Matchers; +}; + +jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { + var parent = this.getMatchersClass_(); + var newMatchersClass = function() { + parent.apply(this, arguments); + }; + jasmine.util.inherit(newMatchersClass, parent); + for (var method in matchersPrototype) { + newMatchersClass.prototype[method] = matchersPrototype[method]; + } + this.matchersClass = newMatchersClass; +}; + +jasmine.Spec.prototype.finishCallback = function() { + this.env.reporter.reportSpecResults(this); +}; + +jasmine.Spec.prototype.finish = function() { + this.safeExecuteAfters(); + + this.removeAllSpies(); + this.finishCallback(); + this.finished = true; +}; + +jasmine.Spec.prototype.after = function(doAfter) { + this.afterCallbacks.unshift(doAfter); +}; + +jasmine.Spec.prototype.execute = function() { + if (!this.env.specFilter(this)) { + this.results.skipped = true; + this.finishCallback(); + this.finished = true; + return; + } + + this.env.currentSpec = this; + this.env.currentlyRunningTests = true; + + this.safeExecuteBefores(); + + if (this.queue[0]) { + this.queue[0].execute(); + } else { + this.finish(); + } + this.env.currentlyRunningTests = false; +}; + +jasmine.Spec.prototype.safeExecuteBefores = function() { + var befores = []; + for (var suite = this.suite; suite; suite = suite.parentSuite) { + if (suite.beforeEachFunction) befores.push(suite.beforeEachFunction); + } + + while (befores.length) { + this.safeExecuteBeforeOrAfter(befores.pop()); + } +}; + +jasmine.Spec.prototype.safeExecuteAfters = function() { + for (var suite = this.suite; suite; suite = suite.parentSuite) { + if (suite.afterEachFunction) this.safeExecuteBeforeOrAfter(suite.afterEachFunction); + } +}; + +jasmine.Spec.prototype.safeExecuteBeforeOrAfter = function(func) { + try { + func.apply(this); + } catch (e) { + this.results.addResult(new jasmine.ExpectationResult(false, func.typeName + '() fail: ' + jasmine.util.formatException(e), null)); + } +}; + +jasmine.Spec.prototype.explodes = function() { + throw 'explodes function should not have been called'; +}; + +jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { + if (obj == undefined) { + throw "spyOn could not find an object to spy upon for " + methodName + "()"; + } + + if (!ignoreMethodDoesntExist && obj[methodName] === undefined) { + throw methodName + '() method does not exist'; + } + + if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { + 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.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_ = []; +}; + jasmine.Matchers = function(env, actual, results) { this.env = env; this.actual = actual; @@ -1302,628 +1893,41 @@ window.clearInterval = function(timeoutKey) { return jasmine.Clock.installed.clearInterval.apply(this, arguments); }; -/** - * @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", "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); - } -})(); -/** - * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults - * - * @constructor - */ -jasmine.NestedResults = function() { - /** - * The total count of results - */ - this.totalCount = 0; - /** - * Number of passed results - */ - this.passedCount = 0; - /** - * Number of failed results - */ - this.failedCount = 0; - /** - * Was this suite/spec skipped? - */ - this.skipped = false; - /** - * @ignore - */ - this.items_ = []; -}; - -/** - * Roll up the result counts. - * - * @param result - */ -jasmine.NestedResults.prototype.rollupCounts = function(result) { - this.totalCount += result.totalCount; - this.passedCount += result.passedCount; - this.failedCount += result.failedCount; -}; - -/** - * Tracks a result's message. - * @param message - */ -jasmine.NestedResults.prototype.log = function(message) { - this.items_.push(new jasmine.MessageResult(message)); -}; - -/** - * Getter for the results: message & results. - */ -jasmine.NestedResults.prototype.getItems = function() { - return this.items_; -}; - -/** - * Adds a result, tracking counts (total, passed, & failed) - * @param {jasmine.ExpectationResult|jasmine.NestedResults} result - */ -jasmine.NestedResults.prototype.addResult = function(result) { - if (result.type != 'MessageResult') { - if (result.items_) { - this.rollupCounts(result); - } else { - this.totalCount++; - if (result.passed) { - this.passedCount++; - } else { - this.failedCount++; - } - } - } - this.items_.push(result); -}; - -/** - * @returns {Boolean} True if everything below passed - */ -jasmine.NestedResults.prototype.passed = function() { - return this.passedCount === this.totalCount; -}; -/** - * Base class for pretty printing for expectation results. - */ -jasmine.PrettyPrinter = function() { - this.ppNestLevel_ = 0; -}; - -/** - * Formats a value in a nice, human-readable string. - * - * @param value - * @returns {String} - */ -jasmine.PrettyPrinter.prototype.format = function(value) { - if (this.ppNestLevel_ > 40) { - // return '(jasmine.pp nested too deeply!)'; - throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); - } - - this.ppNestLevel_++; - try { - if (value === undefined) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value.navigator && value.frames && value.setTimeout) { - this.emitScalar(''); - } else if (value instanceof jasmine.Matchers.Any) { - this.emitScalar(value.toString()); - } else if (typeof value === 'string') { - this.emitScalar("'" + value + "'"); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (typeof value.nodeType === 'number') { - this.emitScalar('HTMLNode'); - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (value.__Jasmine_been_here_before__) { - this.emitScalar(''); - } else if (jasmine.isArray_(value) || typeof value == 'object') { - value.__Jasmine_been_here_before__ = true; - if (jasmine.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - delete value.__Jasmine_been_here_before__; - } else { - this.emitScalar(value.toString()); - } - } finally { - this.ppNestLevel_--; - } -}; - -jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { - for (var property in obj) { - if (property == '__Jasmine_been_here_before__') continue; - fn(property, obj.__lookupGetter__(property) != null); - } -}; - -jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; -jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; - -jasmine.StringPrettyPrinter = function() { - jasmine.PrettyPrinter.call(this); - - this.string = ''; -}; -jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); - -jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { - this.append(value); -}; - -jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { - this.append('[ '); - for (var i = 0; i < array.length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - this.append(' ]'); -}; - -jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { - var self = this; - this.append('{ '); - var first = true; - - this.iterateObject(obj, function(property, isGetter) { - if (first) { - first = false; - } else { - self.append(', '); - } - - self.append(property); - self.append(' : '); - if (isGetter) { - self.append(''); - } else { - self.format(obj[property]); - } - }); - - this.append(' }'); -}; - -jasmine.StringPrettyPrinter.prototype.append = function(value) { - this.string += value; -}; -/** - * QueuedFunction is how ActionCollections' actions are implemented - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {Number} timeout - * @param {Function} latchFunction - * @param {jasmine.Spec} spec - */ -jasmine.QueuedFunction = function(env, func, timeout, latchFunction, spec) { - this.env = env; - this.func = func; +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { this.timeout = timeout; this.latchFunction = latchFunction; - this.spec = spec; - + this.message = message; this.totalTimeSpentWaitingForLatch = 0; - this.latchTimeoutIncrement = 100; + jasmine.Block.call(this, env, null, spec); }; -jasmine.QueuedFunction.prototype.next = function() { - this.spec.finish(); // default value is to be done after one function -}; +jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); -jasmine.QueuedFunction.prototype.safeExecute = function() { - this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...'); +jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 100; - try { - this.func.apply(this.spec); - } catch (e) { - this.fail(e); - } -}; - -jasmine.QueuedFunction.prototype.execute = function() { +jasmine.WaitsForBlock.prototype.execute = function () { var self = this; - var executeNow = function() { - self.safeExecute(); + self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen')); + var latchFunctionResult; + try { + latchFunctionResult = self.latchFunction.apply(self.spec); + } catch (e) { + self.fail(e); self.next(); - }; - - var executeLater = function() { - self.env.setTimeout(executeNow, self.timeout); - }; - - var executeNowOrLater = function() { - var latchFunctionResult; - - try { - latchFunctionResult = self.latchFunction.apply(self.spec); - } catch (e) { - self.fail(e); - self.next(); - return; - } - - if (latchFunctionResult) { - executeNow(); - } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) { - var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.latchFunction.description || 'something to happen'); - self.fail({ - name: 'timeout', - message: message - }); - self.next(); - } else { - self.totalTimeSpentWaitingForLatch += self.latchTimeoutIncrement; - self.env.setTimeout(executeNowOrLater, self.latchTimeoutIncrement); - } - }; - - if (this.latchFunction !== undefined) { - executeNowOrLater(); - } else if (this.timeout > 0) { - executeLater(); - } else { - executeNow(); - } -}; - -jasmine.QueuedFunction.prototype.fail = function(e) { - this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); -}; -/* JasmineReporters.reporter - * Base object that will get called whenever a Spec, Suite, or Runner is done. It is up to - * descendants of this object to do something with the results (see json_reporter.js) - */ -jasmine.Reporters = {}; - -jasmine.Reporters.reporter = function(callbacks) { - var that = { - callbacks: callbacks || {}, - - doCallback: function(callback, results) { - if (callback) { - callback(results); - } - }, - - reportRunnerResults: function(runner) { - that.doCallback(that.callbacks.runnerCallback, runner); - }, - reportSuiteResults: function(suite) { - that.doCallback(that.callbacks.suiteCallback, suite); - }, - reportSpecResults: function(spec) { - that.doCallback(that.callbacks.specCallback, spec); - }, - log: function (str) { - if (console && console.log) console.log(str); - } - }; - - return that; -}; - -/** - * Runner - * - * @constructor - * @param {jasmine.Env} env - */ -jasmine.Runner = function(env) { - jasmine.ActionCollection.call(this, env); - - this.suites = this.actions; -}; -jasmine.util.inherit(jasmine.Runner, jasmine.ActionCollection); - -jasmine.Runner.prototype.execute = function() { - if (this.env.reporter.reportRunnerStarting) { - this.env.reporter.reportRunnerStarting(this); - } - jasmine.ActionCollection.prototype.execute.call(this); -}; - -jasmine.Runner.prototype.finishCallback = function() { - this.env.reporter.reportRunnerResults(this); -}; - -jasmine.Runner.prototype.getResults = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.suites.length; i++) { - results.rollupCounts(this.suites[i].getResults()); - } - return results; -}; -/** - * 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) { - this.id = env.nextSpecId_++; - this.env = env; - this.suite = suite; - this.description = description; - this.queue = []; - this.currentTimeout = 0; - this.currentLatchFunction = undefined; - this.finished = false; - this.afterCallbacks = []; - this.spies_ = []; - - this.results = new jasmine.NestedResults(); - this.results.description = description; - this.runs = this.addToQueue; - this.matchersClass = null; -}; - -jasmine.Spec.prototype.getFullName = function() { - return this.suite.getFullName() + ' ' + this.description + '.'; -}; - -jasmine.Spec.prototype.getResults = function() { - return this.results; -}; - -jasmine.Spec.prototype.addToQueue = function(func) { - var queuedFunction = new jasmine.QueuedFunction(this.env, func, this.currentTimeout, this.currentLatchFunction, this); - this.queue.push(queuedFunction); - - if (this.queue.length > 1) { - var previousQueuedFunction = this.queue[this.queue.length - 2]; - previousQueuedFunction.next = function() { - queuedFunction.execute(); - }; - } - - this.resetTimeout(); - return this; -}; - -/** - * @private - * @deprecated - */ -jasmine.Spec.prototype.expects_that = function(actual) { - return this.expect(actual); -}; - -/** - * @private - */ -jasmine.Spec.prototype.expect = function(actual) { - return new (this.getMatchersClass_())(this.env, actual, this.results); -}; - -/** - * @private - */ -jasmine.Spec.prototype.waits = function(timeout) { - this.currentTimeout = timeout; - this.currentLatchFunction = undefined; - return this; -}; - -/** - * @private - */ -jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, message) { - this.currentTimeout = timeout; - this.currentLatchFunction = latchFunction; - this.currentLatchFunction.description = message; - return this; -}; - -jasmine.Spec.prototype.getMatchersClass_ = function() { - return this.matchersClass || jasmine.Matchers; -}; - -jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { - var parent = this.getMatchersClass_(); - var newMatchersClass = function() { - parent.apply(this, arguments); - }; - jasmine.util.inherit(newMatchersClass, parent); - for (var method in matchersPrototype) { - newMatchersClass.prototype[method] = matchersPrototype[method]; - } - this.matchersClass = newMatchersClass; -}; - -jasmine.Spec.prototype.resetTimeout = function() { - this.currentTimeout = 0; - this.currentLatchFunction = undefined; -}; - -jasmine.Spec.prototype.finishCallback = function() { - this.env.reporter.reportSpecResults(this); -}; - -jasmine.Spec.prototype.finish = function() { - this.safeExecuteAfters(); - - this.removeAllSpies(); - this.finishCallback(); - this.finished = true; -}; - -jasmine.Spec.prototype.after = function(doAfter) { - this.afterCallbacks.unshift(doAfter); -}; - -jasmine.Spec.prototype.execute = function() { - if (!this.env.specFilter(this)) { - this.results.skipped = true; - this.finishCallback(); - this.finished = true; return; } - this.env.currentSpec = this; - this.env.currentlyRunningTests = true; - - this.safeExecuteBefores(); - - if (this.queue[0]) { - this.queue[0].execute(); + if (latchFunctionResult) { + self.next(); + } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) { + var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen'); + self.fail({ + name: 'timeout', + message: message + }); + self.spec.next(); } else { - this.finish(); - } - this.env.currentlyRunningTests = false; -}; - -jasmine.Spec.prototype.safeExecuteBefores = function() { - var befores = []; - for (var suite = this.suite; suite; suite = suite.parentSuite) { - if (suite.beforeEachFunction) befores.push(suite.beforeEachFunction); - } - - while (befores.length) { - this.safeExecuteBeforeOrAfter(befores.pop()); + self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + self.env.setTimeout(function () { self.execute(); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); } }; - -jasmine.Spec.prototype.safeExecuteAfters = function() { - for (var suite = this.suite; suite; suite = suite.parentSuite) { - if (suite.afterEachFunction) this.safeExecuteBeforeOrAfter(suite.afterEachFunction); - } -}; - -jasmine.Spec.prototype.safeExecuteBeforeOrAfter = function(func) { - try { - func.apply(this); - } catch (e) { - this.results.addResult(new jasmine.ExpectationResult(false, func.typeName + '() fail: ' + jasmine.util.formatException(e), null)); - } -}; - -jasmine.Spec.prototype.explodes = function() { - throw 'explodes function should not have been called'; -}; - -jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { - if (obj == undefined) { - throw "spyOn could not find an object to spy upon for " + methodName + "()"; - } - - if (!ignoreMethodDoesntExist && obj[methodName] === undefined) { - throw methodName + '() method does not exist'; - } - - if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { - 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.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_ = []; -}; - -/** - * Internal representation of a Jasmine suite. - * - * @constructor - * @param {jasmine.Env} env - * @param {String} description - * @param {Function} specDefinitions - * @param {jasmine.Suite} parentSuite - */ -jasmine.Suite = function(env, description, specDefinitions, parentSuite) { - jasmine.ActionCollection.call(this, env); - - this.description = description; - this.specs = this.actions; - this.parentSuite = parentSuite; - - this.beforeEachFunction = null; - this.afterEachFunction = null; -}; -jasmine.util.inherit(jasmine.Suite, jasmine.ActionCollection); - -jasmine.Suite.prototype.getFullName = function() { - var fullName = this.description; - for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { - fullName = parentSuite.description + ' ' + fullName; - } - return fullName; -}; - -jasmine.Suite.prototype.finishCallback = function() { - this.env.reporter.reportSuiteResults(this); -}; - -jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { - beforeEachFunction.typeName = 'beforeEach'; - this.beforeEachFunction = beforeEachFunction; -}; - -jasmine.Suite.prototype.afterEach = function(afterEachFunction) { - afterEachFunction.typeName = 'afterEach'; - this.afterEachFunction = afterEachFunction; -}; - -jasmine.Suite.prototype.getResults = function() { - var results = new jasmine.NestedResults(); - for (var i = 0; i < this.specs.length; i++) { - results.rollupCounts(this.specs[i].getResults()); - } - return results; -}; - diff --git a/spec/bootstrap.html b/spec/bootstrap.html index c47b3b12..39fe6514 100755 --- a/spec/bootstrap.html +++ b/spec/bootstrap.html @@ -14,7 +14,9 @@ - + + + diff --git a/spec/runner.html b/spec/runner.html index 41c87bd4..9f9076e4 100644 --- a/spec/runner.html +++ b/spec/runner.html @@ -4,8 +4,6 @@ Jasmine Test Runner - - @@ -15,7 +13,9 @@ - + + + diff --git a/spec/suites/SpecRunningTest.js b/spec/suites/SpecRunningTest.js index ca84e1d4..68f295ba 100644 --- a/spec/suites/SpecRunningTest.js +++ b/spec/suites/SpecRunningTest.js @@ -77,7 +77,7 @@ describe("jasmine spec running", function () { it("should work without a runs block", function() { var another_spec; - var currentSuite = env.describe('default current suite', function() { + env.describe('default current suite', function() { another_spec = env.it('spec with an expectation', function () { var foo = 'bar'; this.expect(foo).toEqual('bar'); @@ -94,12 +94,33 @@ describe("jasmine spec running", function () { expect(another_spec.results.description).toEqual('spec with an expectation'); // "In a spec without a run block, results did not include the spec's description"; }); + it('should queue waits and runs that it encounters while executing specs', function() { + var specWithRunsAndWaits; + env.describe('test async spec', function() { + specWithRunsAndWaits = env.it('spec w/ queued statments', function () { + this.runs(function () { + + }); + this.waits(500); + this.runs(function () { + + }); + this.waits(500); + this.runs(function () { + }); + }); + }); + + expect(specWithRunsAndWaits.queue.length).toEqual(1); + + specWithRunsAndWaits.execute(); + + expect(specWithRunsAndWaits.queue.length).toEqual(6); + }); + it("should run asynchronous tests", function () { var foo = 0; - //set a bogus suite for the spec to attach to - // jasmine.getEnv().currentSuite = {specs: []}; - var a_spec; env.describe('test async spec', function() { a_spec = env.it('simple queue test', function () { @@ -113,11 +134,11 @@ describe("jasmine spec running", function () { }); expect(a_spec.queue.length).toEqual(1, - 'Expected spec queue length to be 1, was ' + a_spec.queue.length); + 'Expected spec queue length to be 1, was ' + a_spec.queue.length); a_spec.execute(); expect(a_spec.queue.length).toEqual(3, - 'Expected spec queue length to be 3, was ' + a_spec.queue.length); + 'Expected spec queue length to be 3, was ' + a_spec.queue.length); foo = 0; env.describe('test async spec', function() { @@ -185,16 +206,16 @@ describe("jasmine spec running", function () { }); }); - expect(another_spec.queue.length).toEqual(1); // 'Calling 2 waits(): Expected queue length to be 1, got ' + another_spec.queue.length; + expect(another_spec.queue.length).toEqual(1); another_spec.execute(); fakeTimer.tick(1000); - expect(another_spec.queue.length).toEqual(4); // 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length; + expect(another_spec.queue.length).toEqual(6); - expect(another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions'; + expect(another_spec.results.getItems().length).toEqual(1); - expect(another_spec.results.getItems()[0].passed).toEqual(true); // 'Calling 2 waits(): Queued expectation failed'; + expect(another_spec.results.getItems()[0].passed).toEqual(true); var baz = 0; var yet_another_spec; @@ -216,9 +237,9 @@ describe("jasmine spec running", function () { yet_another_spec.execute(); fakeTimer.tick(250); - expect(yet_another_spec.queue.length).toEqual(3); // 'Calling 2 waits(): Expected queue length to be 3, got ' + another_spec.queue.length); - expect(yet_another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions'); - expect(yet_another_spec.results.getItems()[0].passed).toEqual(false); // 'Calling 2 waits(): Queued expectation failed'); + expect(yet_another_spec.queue.length).toEqual(4); + expect(yet_another_spec.results.getItems().length).toEqual(1); + expect(yet_another_spec.results.getItems()[0].passed).toEqual(false); }); it("testAsyncSpecsWithMockSuite", function () { @@ -246,9 +267,9 @@ describe("jasmine spec running", function () { another_spec.execute(); fakeTimer.tick(2000); - expect(another_spec.queue.length).toEqual(4); // 'Calling 2 waits(): Expected queue length to be 4, got ' + another_spec.queue.length); - expect(another_spec.results.getItems().length).toEqual(1); // 'Calling 2 waits(): Spec queue did not run all functions'); - expect(another_spec.results.getItems()[0].passed).toEqual(true); // 'Calling 2 waits(): Queued expectation failed'); + expect(another_spec.queue.length).toEqual(6); + expect(another_spec.results.getItems().length).toEqual(1); + expect(another_spec.results.getItems()[0].passed).toEqual(true); }); it("testWaitsFor", function() { @@ -302,7 +323,7 @@ describe("jasmine spec running", function () { expect(actual).toEqual(expected); }); - it("testWaitsForFailsIfTimeout", function() { + it("waitsFor fails and skips the rest of the spec if timeout is reached and the latch function is still false", function() { var runsBlockExecuted = false; var spec; @@ -312,7 +333,7 @@ describe("jasmine spec running", function () { }); this.waitsFor(500, function() { - return false; // force a timeout + return false; }); this.runs(function() { @@ -322,11 +343,11 @@ describe("jasmine spec running", function () { }); spec.execute(); - expect(runsBlockExecuted).toEqual(false, 'should not have executed runs block yet'); + expect(runsBlockExecuted).toEqual(false); fakeTimer.tick(100); - expect(runsBlockExecuted).toEqual(false, 'should not have executed runs block yet'); + expect(runsBlockExecuted).toEqual(false); fakeTimer.tick(400); - expect(runsBlockExecuted).toEqual(false, 'should have timed out, so the second runs block should not have been called'); + expect(runsBlockExecuted).toEqual(false); var actual = spec.results.getItems()[0].message; var expected = 'timeout: timed out after 500 msec waiting for something to happen'; expect(actual).toEqual(expected, @@ -356,11 +377,12 @@ describe("jasmine spec running", function () { describe('test suite declaration', function() { var suite; - var dummyFunction = function() {}; + var dummyFunction = function() { + }; it('should give the suite a description', function() { suite = env.describe('one suite description', dummyFunction); - expect(suite.description).toEqual('one suite description'); // 'Suite did not get a description'); + expect(suite.description).toEqual('one suite description'); }); it('should add tests to suites declared by the passed function', function() { @@ -368,8 +390,8 @@ describe("jasmine spec running", function () { env.it('should be a test'); }); - expect(suite.specs.length).toEqual(1); // 'Suite did not get a spec pushed'); - expect(suite.specs[0].queue.length).toEqual(0); // "Suite's Spec should not have queuedFunctions"); + expect(suite.specs.length).toEqual(1); + expect(suite.specs[0].queue.length).toEqual(0); }); it('should enqueue functions for multipart tests', function() { @@ -382,7 +404,7 @@ describe("jasmine spec running", function () { }); }); - expect(suite.specs[0].queue.length).toEqual(1); // "Suite's spec did not get a function pushed"); + expect(suite.specs[0].queue.length).toEqual(1); }); it('should enqueue functions for multipart tests and support waits, and run any ready runs() blocks', function() { @@ -401,9 +423,9 @@ describe("jasmine spec running", function () { }); }); - expect(suite.specs[0].queue.length).toEqual(1); // "Suite's spec length should have been 1, was " + suite.specs[0].queue.length); + expect(suite.specs[0].queue.length).toEqual(1); suite.execute(); - expect(suite.specs[0].queue.length).toEqual(3); // "Suite's spec length should have been 3, was " + suite.specs[0].queue.length); + expect(suite.specs[0].queue.length).toEqual(4); expect(foo).toEqual(1); expect(bar).toEqual(0); @@ -466,7 +488,7 @@ describe("jasmine spec running", function () { }); suiteWithAfter.execute(); - var suite = suiteWithAfter; + suite = suiteWithAfter; expect(suite.afterEachFunction); // "testBeforeAndAfterCallbacks: Suite's afterEach was not defined"); expect(suite.specs[0].results.getItems()[0].passed).toEqual(true); // "testBeforeAndAfterCallbacks: afterEach failure: " + suite.results.getItems()[0].results[0].message); expect(suite.specs[0].foo).toEqual(0); // "testBeforeAndAfterCallbacks: afterEach failure: foo was not reset to 0"); @@ -475,6 +497,131 @@ describe("jasmine spec running", function () { }); + it('#waits should allow consecutive waits calls', function () { + var foo = 0; + var waitsSuite = env.describe('suite that waits', function () { + env.it('should wait', function() { + this.waits(500); + this.waits(500); + this.runs(function () { + foo++; + }); + }); + }); + + waitsSuite.execute(); + expect(foo).toEqual(0); + fakeTimer.tick(500); + expect(foo).toEqual(0); + fakeTimer.tick(500); + + expect(foo).toEqual(1); + }); + + describe('#waitsFor should allow consecutive calls', function () { + var foo; + beforeEach(function () { + foo = 0; + }); + + it('exits immediately (does not stack) if the latchFunction times out', function () { + var reachedFirstWaitsFor = false; + var reachedSecondWaitsFor = false; + var waitsSuite = env.describe('suite that waits', function () { + env.it('should stack timeouts', function() { + this.waitsFor(500, function () { reachedFirstWaitsFor = true; return false; }); + this.waitsFor(500, function () { reachedSecondWaitsFor = true;}); + this.runs(function () { + foo++; + }); + }); + }); + + expect(reachedFirstWaitsFor).toEqual(false); + waitsSuite.execute(); + expect(reachedFirstWaitsFor).toEqual(true); + expect(foo).toEqual(0); + expect(reachedSecondWaitsFor).toEqual(false); + fakeTimer.tick(500); + expect(reachedSecondWaitsFor).toEqual(false); + expect(foo).toEqual(0); + fakeTimer.tick(500); + expect(reachedSecondWaitsFor).toEqual(false); + expect(foo).toEqual(0); + }); + + it('stacks latchFunctions', function () { + var firstWaitsResult = false; + var secondWaitsResult = false; + var waitsSuite = env.describe('suite that waits', function () { + env.it('spec with waitsFors', function() { + this.waitsFor(600, function () { fakeTimer.setTimeout(function () {firstWaitsResult = true; }, 300); return firstWaitsResult; }); + this.waitsFor(600, function () { fakeTimer.setTimeout(function () {secondWaitsResult = true; }, 300); return secondWaitsResult; }); + this.runs(function () { + foo++; + }); + }); + }); + + expect(firstWaitsResult).toEqual(false); + expect(secondWaitsResult).toEqual(false); + waitsSuite.execute(); + + expect(firstWaitsResult).toEqual(false); + expect(secondWaitsResult).toEqual(false); + expect(foo).toEqual(0); + + fakeTimer.tick(300); + + expect(firstWaitsResult).toEqual(true); + expect(secondWaitsResult).toEqual(false); + expect(foo).toEqual(0); + + fakeTimer.tick(300); + + expect(firstWaitsResult).toEqual(true); + expect(secondWaitsResult).toEqual(true); + expect(foo).toEqual(1); + + }); + }); + + xit("#beforeEach should be able to eval runs and waits blocks", function () { + + var foo = 0; + var bar = 0; + var suiteWithBefore = env.describe('one suite with a before', function () { + this.beforeEach(function () { + this.runs(function () { + foo++; + }); + this.waits(500); + this.runs(function () { + foo++; + }); + this.waits(500); + }); + + env.it('should be a spec', function () { + bar = 1; + foo++; + }); + + }); + + //expect(foo).toEqual(0); + expect(bar).toEqual(0); + suiteWithBefore.execute(); + expect(bar).toEqual(0); + //expect(foo).toEqual(1); + fakeTimer.tick(500); + expect(bar).toEqual(0); + //expect(foo).toEqual(2); + fakeTimer.tick(500); + expect(bar).toEqual(1); + //expect(foo).toEqual(3); + }); + it("testBeforeExecutesSafely", function() { var report = ""; var suite = env.describe('before fails on first test, passes on second', function() { @@ -634,7 +781,8 @@ describe("jasmine spec running", function () { env.describe('Test Subject', function() { env.describe('when under circumstance A', function() { env.describe('and circumstance B', function() { - nestedSpec = env.it('behaves thusly', function() {}); + nestedSpec = env.it('behaves thusly', function() { + }); }); }); }); @@ -740,7 +888,7 @@ describe("jasmine spec running", function () { }); spec1 = env.it('spec with an expectation').runs(function () { - this.addMatchers( { matcherForSpec: function(expected) { + this.addMatchers({ matcherForSpec: function(expected) { return "matcherForSpec: actual: " + this.actual + "; expected: " + expected; } }); spec1Matcher = this.expect("xxx"); diff --git a/src/Block.js b/src/Block.js new file mode 100644 index 00000000..dabd4ca5 --- /dev/null +++ b/src/Block.js @@ -0,0 +1,31 @@ +/** + * 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.next = function() { + this.spec.finish(); // default value is to be done after one function +}; + +jasmine.Block.prototype.execute = function() { + this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...'); + try { + this.func.apply(this.spec); + } catch (e) { + this.fail(e); + } + this.next(); +}; + +jasmine.Block.prototype.fail = function(e) { + this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); +}; \ No newline at end of file diff --git a/src/QueuedFunction.js b/src/QueuedFunction.js deleted file mode 100644 index e4845c38..00000000 --- a/src/QueuedFunction.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * QueuedFunction is how ActionCollections' actions are implemented - * - * @constructor - * @param {jasmine.Env} env - * @param {Function} func - * @param {Number} timeout - * @param {Function} latchFunction - * @param {jasmine.Spec} spec - */ -jasmine.QueuedFunction = function(env, func, timeout, latchFunction, spec) { - this.env = env; - this.func = func; - this.timeout = timeout; - this.latchFunction = latchFunction; - this.spec = spec; - - this.totalTimeSpentWaitingForLatch = 0; - this.latchTimeoutIncrement = 100; -}; - -jasmine.QueuedFunction.prototype.next = function() { - this.spec.finish(); // default value is to be done after one function -}; - -jasmine.QueuedFunction.prototype.safeExecute = function() { - this.env.reporter.log('>> Jasmine Running ' + this.spec.suite.description + ' ' + this.spec.description + '...'); - - try { - this.func.apply(this.spec); - } catch (e) { - this.fail(e); - } -}; - -jasmine.QueuedFunction.prototype.execute = function() { - var self = this; - var executeNow = function() { - self.safeExecute(); - self.next(); - }; - - var executeLater = function() { - self.env.setTimeout(executeNow, self.timeout); - }; - - var executeNowOrLater = function() { - var latchFunctionResult; - - try { - latchFunctionResult = self.latchFunction.apply(self.spec); - } catch (e) { - self.fail(e); - self.next(); - return; - } - - if (latchFunctionResult) { - executeNow(); - } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) { - var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.latchFunction.description || 'something to happen'); - self.fail({ - name: 'timeout', - message: message - }); - self.next(); - } else { - self.totalTimeSpentWaitingForLatch += self.latchTimeoutIncrement; - self.env.setTimeout(executeNowOrLater, self.latchTimeoutIncrement); - } - }; - - if (this.latchFunction !== undefined) { - executeNowOrLater(); - } else if (this.timeout > 0) { - executeLater(); - } else { - executeNow(); - } -}; - -jasmine.QueuedFunction.prototype.fail = function(e) { - this.spec.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); -}; diff --git a/src/Spec.js b/src/Spec.js index d754382f..ab443cc5 100644 --- a/src/Spec.js +++ b/src/Spec.js @@ -12,8 +12,6 @@ jasmine.Spec = function(env, suite, description) { this.suite = suite; this.description = description; this.queue = []; - this.currentTimeout = 0; - this.currentLatchFunction = undefined; this.finished = false; this.afterCallbacks = []; this.spies_ = []; @@ -33,17 +31,12 @@ jasmine.Spec.prototype.getResults = function() { }; jasmine.Spec.prototype.addToQueue = function(func) { - var queuedFunction = new jasmine.QueuedFunction(this.env, func, this.currentTimeout, this.currentLatchFunction, this); - this.queue.push(queuedFunction); + var block = new jasmine.Block(this.env, func, this); - if (this.queue.length > 1) { - var previousQueuedFunction = this.queue[this.queue.length - 2]; - previousQueuedFunction.next = function() { - queuedFunction.execute(); - }; - } + this.setNextOnLastInQueue(block); + + this.queue.push(block); - this.resetTimeout(); return this; }; @@ -56,31 +49,48 @@ jasmine.Spec.prototype.expects_that = function(actual) { }; /** - * @private +* @private */ jasmine.Spec.prototype.expect = function(actual) { return new (this.getMatchersClass_())(this.env, actual, this.results); }; +/** + * @private + */ +jasmine.Spec.prototype.setNextOnLastInQueue = function (block) { + if (this.queue.length > 0) { + var previousBlock = this.queue[this.queue.length - 1]; + previousBlock.next = function() { + block.execute(); + }; + } +}; + /** * @private */ jasmine.Spec.prototype.waits = function(timeout) { - this.currentTimeout = timeout; - this.currentLatchFunction = undefined; + var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); + this.setNextOnLastInQueue(waitsFunc); + this.queue.push(waitsFunc); return this; }; /** * @private */ -jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, message) { - this.currentTimeout = timeout; - this.currentLatchFunction = latchFunction; - this.currentLatchFunction.description = message; +jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) { + var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this); + this.setNextOnLastInQueue(waitsForFunc); + this.queue.push(waitsForFunc); return this; }; +jasmine.Spec.prototype.failWithException = function (e) { + this.results.addResult(new jasmine.ExpectationResult(false, jasmine.util.formatException(e), null)); +}; + jasmine.Spec.prototype.getMatchersClass_ = function() { return this.matchersClass || jasmine.Matchers; }; @@ -97,11 +107,6 @@ jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { this.matchersClass = newMatchersClass; }; -jasmine.Spec.prototype.resetTimeout = function() { - this.currentTimeout = 0; - this.currentLatchFunction = undefined; -}; - jasmine.Spec.prototype.finishCallback = function() { this.env.reporter.reportSpecResults(this); }; diff --git a/src/WaitsBlock.js b/src/WaitsBlock.js new file mode 100644 index 00000000..58c0be97 --- /dev/null +++ b/src/WaitsBlock.js @@ -0,0 +1,14 @@ +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 () { + var self = this; + self.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); + self.env.setTimeout(function () { + self.next(); + }, self.timeout); +}; diff --git a/src/WaitsForBlock.js b/src/WaitsForBlock.js new file mode 100644 index 00000000..dd9a00d4 --- /dev/null +++ b/src/WaitsForBlock.js @@ -0,0 +1,38 @@ +jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { + this.timeout = timeout; + 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 = 100; + +jasmine.WaitsForBlock.prototype.execute = function () { + var self = this; + self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen')); + var latchFunctionResult; + try { + latchFunctionResult = self.latchFunction.apply(self.spec); + } catch (e) { + self.fail(e); + self.next(); + return; + } + + if (latchFunctionResult) { + self.next(); + } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) { + var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen'); + self.fail({ + name: 'timeout', + message: message + }); + self.spec.next(); + } else { + self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; + self.env.setTimeout(function () { self.execute(); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); + } +}; \ No newline at end of file