1 /** 2 * Internal representation of a Jasmine specification, or test. 3 * 4 * @constructor 5 * @param {jasmine.Env} env 6 * @param {jasmine.Suite} suite 7 * @param {String} description 8 */ 9 jasmine.Spec = function(env, suite, description) { 10 if (!env) { 11 throw new Error('jasmine.Env() required'); 12 } 13 if (!suite) { 14 throw new Error('jasmine.Suite() required'); 15 } 16 var spec = this; 17 spec.id = env.nextSpecId ? env.nextSpecId() : null; 18 spec.env = env; 19 spec.suite = suite; 20 spec.description = description; 21 spec.queue = new jasmine.Queue(env); 22 23 spec.afterCallbacks = []; 24 spec.spies_ = []; 25 26 spec.results_ = new jasmine.NestedResults(); 27 spec.results_.description = description; 28 spec.matchersClass = null; 29 }; 30 31 jasmine.Spec.prototype.getFullName = function() { 32 return this.suite.getFullName() + ' ' + this.description + '.'; 33 }; 34 35 36 jasmine.Spec.prototype.results = function() { 37 return this.results_; 38 }; 39 40 /** 41 * All parameters are pretty-printed and concatenated together, then written to the spec's output. 42 * 43 * Be careful not to leave calls to <code>jasmine.log</code> in production code. 44 */ 45 jasmine.Spec.prototype.log = function() { 46 return this.results_.log(arguments); 47 }; 48 49 jasmine.Spec.prototype.runs = function (func) { 50 var block = new jasmine.Block(this.env, func, this); 51 this.addToQueue(block); 52 return this; 53 }; 54 55 jasmine.Spec.prototype.addToQueue = function (block) { 56 if (this.queue.isRunning()) { 57 this.queue.insertNext(block); 58 } else { 59 this.queue.add(block); 60 } 61 }; 62 63 /** 64 * @param {jasmine.ExpectationResult} result 65 */ 66 jasmine.Spec.prototype.addMatcherResult = function(result) { 67 this.results_.addResult(result); 68 }; 69 70 jasmine.Spec.prototype.expect = function(actual) { 71 var positive = new (this.getMatchersClass_())(this.env, actual, this); 72 positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); 73 return positive; 74 }; 75 76 /** 77 * Waits a fixed time period before moving to the next block. 78 * 79 * @deprecated Use waitsFor() instead 80 * @param {Number} timeout milliseconds to wait 81 */ 82 jasmine.Spec.prototype.waits = function(timeout) { 83 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 84 this.addToQueue(waitsFunc); 85 return this; 86 }; 87 88 /** 89 * Waits for the latchFunction to return true before proceeding to the next block. 90 * 91 * @param {Function} latchFunction 92 * @param {String} optional_timeoutMessage 93 * @param {Number} optional_timeout 94 */ 95 jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 96 var latchFunction_ = null; 97 var optional_timeoutMessage_ = null; 98 var optional_timeout_ = null; 99 100 for (var i = 0; i < arguments.length; i++) { 101 var arg = arguments[i]; 102 switch (typeof arg) { 103 case 'function': 104 latchFunction_ = arg; 105 break; 106 case 'string': 107 optional_timeoutMessage_ = arg; 108 break; 109 case 'number': 110 optional_timeout_ = arg; 111 break; 112 } 113 } 114 115 var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); 116 this.addToQueue(waitsForFunc); 117 return this; 118 }; 119 120 jasmine.Spec.prototype.fail = function (e) { 121 var expectationResult = new jasmine.ExpectationResult({ 122 passed: false, 123 message: e ? jasmine.util.formatException(e) : 'Exception' 124 }); 125 this.results_.addResult(expectationResult); 126 }; 127 128 jasmine.Spec.prototype.getMatchersClass_ = function() { 129 return this.matchersClass || this.env.matchersClass; 130 }; 131 132 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 133 var parent = this.getMatchersClass_(); 134 var newMatchersClass = function() { 135 parent.apply(this, arguments); 136 }; 137 jasmine.util.inherit(newMatchersClass, parent); 138 jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); 139 this.matchersClass = newMatchersClass; 140 }; 141 142 jasmine.Spec.prototype.finishCallback = function() { 143 this.env.reporter.reportSpecResults(this); 144 }; 145 146 jasmine.Spec.prototype.finish = function(onComplete) { 147 this.removeAllSpies(); 148 this.finishCallback(); 149 if (onComplete) { 150 onComplete(); 151 } 152 }; 153 154 jasmine.Spec.prototype.after = function(doAfter) { 155 if (this.queue.isRunning()) { 156 this.queue.add(new jasmine.Block(this.env, doAfter, this)); 157 } else { 158 this.afterCallbacks.unshift(doAfter); 159 } 160 }; 161 162 jasmine.Spec.prototype.execute = function(onComplete) { 163 var spec = this; 164 if (!spec.env.specFilter(spec)) { 165 spec.results_.skipped = true; 166 spec.finish(onComplete); 167 return; 168 } 169 170 this.env.reporter.reportSpecStarting(this); 171 172 spec.env.currentSpec = spec; 173 174 spec.addBeforesAndAftersToQueue(); 175 176 spec.queue.start(function () { 177 spec.finish(onComplete); 178 }); 179 }; 180 181 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 182 var runner = this.env.currentRunner(); 183 var i; 184 185 for (var suite = this.suite; suite; suite = suite.parentSuite) { 186 for (i = 0; i < suite.before_.length; i++) { 187 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 188 } 189 } 190 for (i = 0; i < runner.before_.length; i++) { 191 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 192 } 193 for (i = 0; i < this.afterCallbacks.length; i++) { 194 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); 195 } 196 for (suite = this.suite; suite; suite = suite.parentSuite) { 197 for (i = 0; i < suite.after_.length; i++) { 198 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); 199 } 200 } 201 for (i = 0; i < runner.after_.length; i++) { 202 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); 203 } 204 }; 205 206 jasmine.Spec.prototype.explodes = function() { 207 throw 'explodes function should not have been called'; 208 }; 209 210 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 211 if (obj == jasmine.undefined) { 212 throw "spyOn could not find an object to spy upon for " + methodName + "()"; 213 } 214 215 if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { 216 throw methodName + '() method does not exist'; 217 } 218 219 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 220 throw new Error(methodName + ' has already been spied upon'); 221 } 222 223 var spyObj = jasmine.createSpy(methodName); 224 225 this.spies_.push(spyObj); 226 spyObj.baseObj = obj; 227 spyObj.methodName = methodName; 228 spyObj.originalValue = obj[methodName]; 229 230 obj[methodName] = spyObj; 231 232 return spyObj; 233 }; 234 235 jasmine.Spec.prototype.removeAllSpies = function() { 236 for (var i = 0; i < this.spies_.length; i++) { 237 var spy = this.spies_[i]; 238 spy.baseObj[spy.methodName] = spy.originalValue; 239 } 240 this.spies_ = []; 241 }; 242 243