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