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