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 
130   if (func) {
131     spec.runs(func);
132   }
133 
134   return spec;
135 };
136 
137 jasmine.Env.prototype.xit = function(desc, func) {
138   return {
139     id: this.nextSpecId(),
140     runs: function() {
141     }
142   };
143 };
144 
145 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {
146   if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {
147     return true;
148   }
149 
150   a.__Jasmine_been_here_before__ = b;
151   b.__Jasmine_been_here_before__ = a;
152 
153   var hasKey = function(obj, keyName) {
154     return obj != null && obj[keyName] !== jasmine.undefined;
155   };
156 
157   for (var property in b) {
158     if (!hasKey(a, property) && hasKey(b, property)) {
159       mismatchKeys.push("expected has key '" + property + "', but missing from actual.");
160     }
161   }
162   for (property in a) {
163     if (!hasKey(b, property) && hasKey(a, property)) {
164       mismatchKeys.push("expected missing key '" + property + "', but present in actual.");
165     }
166   }
167   for (property in b) {
168     if (property == '__Jasmine_been_here_before__') continue;
169     if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {
170       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.");
171     }
172   }
173 
174   if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {
175     mismatchValues.push("arrays were not the same length");
176   }
177 
178   delete a.__Jasmine_been_here_before__;
179   delete b.__Jasmine_been_here_before__;
180   return (mismatchKeys.length == 0 && mismatchValues.length == 0);
181 };
182 
183 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {
184   mismatchKeys = mismatchKeys || [];
185   mismatchValues = mismatchValues || [];
186 
187   if (a === b) return true;
188 
189   if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {
190     return (a == jasmine.undefined && b == jasmine.undefined);
191   }
192 
193   if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {
194     return a === b;
195   }
196 
197   if (a instanceof Date && b instanceof Date) {
198     return a.getTime() == b.getTime();
199   }
200 
201   if (a instanceof jasmine.Matchers.Any) {
202     return a.matches(b);
203   }
204 
205   if (b instanceof jasmine.Matchers.Any) {
206     return b.matches(a);
207   }
208 
209   if (typeof a === "object" && typeof b === "object") {
210     return this.compareObjects_(a, b, mismatchKeys, mismatchValues);
211   }
212 
213   for (var i = 0; i < this.equalityTesters_.length; i++) {
214     var equalityTester = this.equalityTesters_[i];
215     var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);
216     if (result !== jasmine.undefined) return result;
217   }
218 
219   //Straight check
220   return (a === b);
221 };
222 
223 jasmine.Env.prototype.contains_ = function(haystack, needle) {
224   if (jasmine.isArray_(haystack)) {
225     for (var i = 0; i < haystack.length; i++) {
226       if (this.equals_(haystack[i], needle)) return true;
227     }
228     return false;
229   }
230   return haystack.indexOf(needle) >= 0;
231 };
232 
233 jasmine.Env.prototype.addEqualityTester = function(equalityTester) {
234   this.equalityTesters_.push(equalityTester);
235 };
236