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 jasmine.Spec.prototype.log = function() {
 41   return this.results_.log(arguments);
 42 };
 43 
 44 jasmine.Spec.prototype.runs = function (func) {
 45   var block = new jasmine.Block(this.env, func, this);
 46   this.addToQueue(block);
 47   return this;
 48 };
 49 
 50 jasmine.Spec.prototype.addToQueue = function (block) {
 51   if (this.queue.isRunning()) {
 52     this.queue.insertNext(block);
 53   } else {
 54     this.queue.add(block);
 55   }
 56 };
 57 
 58 /**
 59  * @param {jasmine.ExpectationResult} result
 60  */
 61 jasmine.Spec.prototype.addMatcherResult = function(result) {
 62   this.results_.addResult(result);
 63 };
 64 
 65 jasmine.Spec.prototype.expect = function(actual) {
 66   var positive = new (this.getMatchersClass_())(this.env, actual, this);
 67   positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);
 68   return positive;
 69 };
 70 
 71 jasmine.Spec.prototype.waits = function(timeout) {
 72   var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);
 73   this.addToQueue(waitsFunc);
 74   return this;
 75 };
 76 
 77 jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) {
 78   var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this);
 79   this.addToQueue(waitsForFunc);
 80   return this;
 81 };
 82 
 83 jasmine.Spec.prototype.fail = function (e) {
 84   var expectationResult = new jasmine.ExpectationResult({
 85     passed: false,
 86     message: e ? jasmine.util.formatException(e) : 'Exception'
 87   });
 88   this.results_.addResult(expectationResult);
 89 };
 90 
 91 jasmine.Spec.prototype.getMatchersClass_ = function() {
 92   return this.matchersClass || this.env.matchersClass;
 93 };
 94 
 95 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {
 96   var parent = this.getMatchersClass_();
 97   var newMatchersClass = function() {
 98     parent.apply(this, arguments);
 99   };
100   jasmine.util.inherit(newMatchersClass, parent);
101   jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);
102   this.matchersClass = newMatchersClass;
103 };
104 
105 jasmine.Spec.prototype.finishCallback = function() {
106   this.env.reporter.reportSpecResults(this);
107 };
108 
109 jasmine.Spec.prototype.finish = function(onComplete) {
110   this.removeAllSpies();
111   this.finishCallback();
112   if (onComplete) {
113     onComplete();
114   }
115 };
116 
117 jasmine.Spec.prototype.after = function(doAfter) {
118   if (this.queue.isRunning()) {
119     this.queue.add(new jasmine.Block(this.env, doAfter, this));
120   } else {
121     this.afterCallbacks.unshift(doAfter);
122   }
123 };
124 
125 jasmine.Spec.prototype.execute = function(onComplete) {
126   var spec = this;
127   if (!spec.env.specFilter(spec)) {
128     spec.results_.skipped = true;
129     spec.finish(onComplete);
130     return;
131   }
132   this.env.reporter.log('>> Jasmine Running ' + this.suite.description + ' ' + this.description + '...');
133 
134   spec.env.currentSpec = spec;
135 
136   spec.addBeforesAndAftersToQueue();
137 
138   spec.queue.start(function () {
139     spec.finish(onComplete);
140   });
141 };
142 
143 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {
144   var runner = this.env.currentRunner();
145   var i;
146 
147   for (var suite = this.suite; suite; suite = suite.parentSuite) {
148     for (i = 0; i < suite.before_.length; i++) {
149       this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));
150     }
151   }
152   for (i = 0; i < runner.before_.length; i++) {
153     this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));
154   }
155   for (i = 0; i < this.afterCallbacks.length; i++) {
156     this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this));
157   }
158   for (suite = this.suite; suite; suite = suite.parentSuite) {
159     for (i = 0; i < suite.after_.length; i++) {
160       this.queue.add(new jasmine.Block(this.env, suite.after_[i], this));
161     }
162   }
163   for (i = 0; i < runner.after_.length; i++) {
164     this.queue.add(new jasmine.Block(this.env, runner.after_[i], this));
165   }
166 };
167 
168 jasmine.Spec.prototype.explodes = function() {
169   throw 'explodes function should not have been called';
170 };
171 
172 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {
173   if (obj == jasmine.undefined) {
174     throw "spyOn could not find an object to spy upon for " + methodName + "()";
175   }
176 
177   if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {
178     throw methodName + '() method does not exist';
179   }
180 
181   if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {
182     throw new Error(methodName + ' has already been spied upon');
183   }
184 
185   var spyObj = jasmine.createSpy(methodName);
186 
187   this.spies_.push(spyObj);
188   spyObj.baseObj = obj;
189   spyObj.methodName = methodName;
190   spyObj.originalValue = obj[methodName];
191 
192   obj[methodName] = spyObj;
193 
194   return spyObj;
195 };
196 
197 jasmine.Spec.prototype.removeAllSpies = function() {
198   for (var i = 0; i < this.spies_.length; i++) {
199     var spy = this.spies_[i];
200     spy.baseObj[spy.methodName] = spy.originalValue;
201   }
202   this.spies_ = [];
203 };
204 
205