1 /**
  2  * Environment for Jasmine
  3  *
  4  * @constructor
  5  */
  6 jasmine.Env = function() {
  7   this.currentSpec = null;
  8   this.currentSuite = null;
  9   this.currentRunner_ = new jasmine.Runner(this);
 10 
 11   this.reporter = new jasmine.MultiReporter();
 12 
 13   this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;
 14   this.lastUpdate = 0;
 15   this.specFilter = function() {
 16     return true;
 17   };
 18 
 19   this.nextSpecId_ = 0;
 20   this.nextSuiteId_ = 0;
 21   this.equalityTesters_ = [];
 22 
 23   // wrap matchers
 24   this.matchersClass = function() {
 25     jasmine.Matchers.apply(this, arguments);
 26   };
 27   jasmine.util.inherit(this.matchersClass, jasmine.Matchers);
 28 
 29   for (var methodName in jasmine.Matchers.prototype) {
 30     var orig = jasmine.Matchers.prototype[methodName];
 31     this.matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);
 32   }
 33 };
 34 
 35 
 36 jasmine.Env.prototype.setTimeout = jasmine.setTimeout;
 37 jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;
 38 jasmine.Env.prototype.setInterval = jasmine.setInterval;
 39 jasmine.Env.prototype.clearInterval = jasmine.clearInterval;
 40 
 41 /**
 42  * @returns an object containing jasmine version build info, if set.
 43  */
 44 jasmine.Env.prototype.version = function () {
 45   if (jasmine.version_) {
 46     return jasmine.version_;
 47   } else {
 48     throw new Error('Version not set');
 49   }
 50 };
 51 
 52 /**
 53  * @returns a sequential integer starting at 0
 54  */
 55 jasmine.Env.prototype.nextSpecId = function () {
 56   return this.nextSpecId_++;
 57 };
 58 
 59 /**
 60  * @returns a sequential integer starting at 0
 61  */
 62 jasmine.Env.prototype.nextSuiteId = function () {
 63   return this.nextSuiteId_++;
 64 };
 65 
 66 /**
 67  * Register a reporter to receive status updates from Jasmine.
 68  * @param {jasmine.Reporter} reporter An object which will receive status updates.
 69  */
 70 jasmine.Env.prototype.addReporter = function(reporter) {
 71   this.reporter.addReporter(reporter);
 72 };
 73 
 74 jasmine.Env.prototype.execute = function() {
 75   this.currentRunner_.execute();
 76 };
 77 
 78 jasmine.Env.prototype.describe = function(description, specDefinitions) {
 79   var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);
 80 
 81   var parentSuite = this.currentSuite;
 82   if (parentSuite) {
 83     parentSuite.add(suite);
 84   } else {
 85     this.currentRunner_.add(suite);
 86   }
 87 
 88   this.currentSuite = suite;
 89 
 90   specDefinitions.call(suite);
 91 
 92   this.currentSuite = parentSuite;
 93 
 94   return suite;
 95 };
 96 
 97 jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {
 98   if (this.currentSuite) {
 99     this.currentSuite.beforeEach(beforeEachFunction);
100   } else {
101     this.currentRunner_.beforeEach(beforeEachFunction);
102   }
103 };
104 
105 jasmine.Env.prototype.currentRunner = function () {
106   return this.currentRunner_;
107 };
108 
109 jasmine.Env.prototype.afterEach = function(afterEachFunction) {
110   if (this.currentSuite) {
111     this.currentSuite.afterEach(afterEachFunction);
112   } else {
113     this.currentRunner_.afterEach(afterEachFunction);
114   }
115 
116 };
117 
118 jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {
119   return {
120     execute: function() {
121     }
122   };
123 };
124 
125 jasmine.Env.prototype.it = function(description, func) {
126   var spec = new jasmine.Spec(this, this.currentSuite, description);
127   this.currentSuite.add(spec);
128   this.currentSpec = spec;
129   spec.pending = false;
130 
131   if (func) {
132     spec.runs(func);
133   } else {
134     spec.pending = true;
135   }
136 
137   return spec;
138 };
139 
140 jasmine.Env.prototype.xit = function(description, func) {
141   var spec = this.it(description, func);
142   spec.pending = -1;
143   return spec;
144 };
145 
146 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
147   if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
148     return true;
149   }
150 
151   a.__Jasmine_been_here_before__ = b;
152   b.__Jasmine_been_here_before__ = a;
153 
154   var hasKey = function(obj, keyName) {
155     return obj != null && obj[keyName] !== undefined;
156   };
157 
158   for (var property in b) {
159     if (!hasKey(a, property) && hasKey(b, property)) {
160       mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
161     }
162   }
163   for (property in a) {
164     if (!hasKey(b, property) && hasKey(a, property)) {
165       mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
166     }
167   }
168   for (property in b) {
169     if (property == '__Jasmine_been_here_before__') continue;
170     if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
171       mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual.");
172     }
173   }
174 
175   if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
176     mismatchValues.push("arrays were not the same length");
177   }
178 
179   delete a.__Jasmine_been_here_before__;
180   delete b.__Jasmine_been_here_before__;
181   return (mismatchKeys.length == 0 && mismatchValues.length == 0);
182 };
183 
184 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
185   mismatchKeys = mismatchKeys || [];
186   mismatchValues = mismatchValues || [];
187 
188   if (a === b) return true;
189 
190   if (a === undefined || a === null || b === undefined || b === null) {
191     return (a == undefined && b == undefined);
192   }
193 
194   if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
195     return a === b;
196   }
197 
198   if (a instanceof Date && b instanceof Date) {
199     return a.getTime() == b.getTime();
200   }
201 
202   if (a instanceof jasmine.Matchers.Any) {
203     return a.matches(b);
204   }
205 
206   if (b instanceof jasmine.Matchers.Any) {
207     return b.matches(a);
208   }
209 
210   if (typeof a === "object" && typeof b === "object") {
211     return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
212   }
213 
214   for (var i = 0; i < this.equalityTesters_.length; i++) {
215     var equalityTester = this.equalityTesters_[i];
216     var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
217     if (result !== undefined) return result;
218   }
219 
220   //Straight check
221   return (a === b);
222 };
223 
224 jasmine.Env.prototype.contains_ = function(haystack, needle) {
225   if (jasmine.isArray_(haystack)) {
226     for (var i = 0; i < haystack.length; i++) {
227       if (this.equals_(haystack[i], needle)) return true;
228     }
229     return false;
230   }
231   return haystack.indexOf(needle) >= 0;
232 };
233 
234 jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
235   this.equalityTesters_.push(equalityTester);
236 };
237