1 /**
  2  * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
  3  *
  4  * @namespace
  5  */
  6 var jasmine = {};
  7 
  8 /**
  9  * @private
 10  */
 11 jasmine.unimplementedMethod_ = function() {
 12   throw new Error("unimplemented method");
 13 };
 14 
 15 /**
 16  * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
 17  * a plain old variable and may be redefined by somebody else.
 18  *
 19  * @private
 20  */
 21 jasmine.undefined = jasmine.___undefined___;
 22 
 23 /**
 24  * Default interval for event loop yields. Small values here may result in slow test running. Zero means no updates until all tests have completed.
 25  *
 26  */
 27 jasmine.DEFAULT_UPDATE_INTERVAL = 250;
 28 
 29 jasmine.getGlobal = function() {
 30   function getGlobal() {
 31     return this;
 32   }
 33 
 34   return getGlobal();
 35 };
 36 
 37 /**
 38  * Allows for bound functions to be compared.  Internal use only.
 39  *
 40  * @ignore
 41  * @private
 42  * @param base {Object} bound 'this' for the function
 43  * @param name {Function} function to find
 44  */
 45 jasmine.bindOriginal_ = function(base, name) {
 46   var original = base[name];
 47   if (original.apply) {
 48     return function() {
 49       return original.apply(base, arguments);
 50     };
 51   } else {
 52     // IE support
 53     return window[name];
 54   }
 55 };
 56 
 57 jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');
 58 jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');
 59 jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');
 60 jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');
 61 
 62 jasmine.MessageResult = function(values) {
 63   this.type = 'log';
 64   this.values = values;
 65   this.trace = new Error(); // todo: test better
 66 };
 67 
 68 jasmine.MessageResult.prototype.toString = function() {
 69   var text = "";
 70   for(var i = 0; i < this.values.length; i++) {
 71     if (i > 0) text += " ";
 72     if (jasmine.isString_(this.values[i])) {
 73       text += this.values[i];
 74     } else {
 75       text += jasmine.pp(this.values[i]);
 76     }
 77   }
 78   return text;
 79 };
 80 
 81 jasmine.ExpectationResult = function(params) {
 82   this.type = 'expect';
 83   this.matcherName = params.matcherName;
 84   this.passed_ = params.passed;
 85   this.expected = params.expected;
 86   this.actual = params.actual;
 87 
 88   this.message = this.passed_ ? 'Passed.' : params.message;
 89   this.trace = this.passed_ ? '' : new Error(this.message);
 90 };
 91 
 92 jasmine.ExpectationResult.prototype.toString = function () {
 93   return this.message;
 94 };
 95 
 96 jasmine.ExpectationResult.prototype.passed = function () {
 97   return this.passed_;
 98 };
 99 
100 /**
101  * Getter for the Jasmine environment. Ensures one gets created
102  */
103 jasmine.getEnv = function() {
104   return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();
105 };
106 
107 /**
108  * @ignore
109  * @private
110  * @param value
111  * @returns {Boolean}
112  */
113 jasmine.isArray_ = function(value) {
114   return jasmine.isA_("Array", value);  
115 };
116 
117 /**
118  * @ignore
119  * @private
120  * @param value
121  * @returns {Boolean}
122  */
123 jasmine.isString_ = function(value) {
124   return jasmine.isA_("String", value);
125 };
126 
127 /**
128  * @ignore
129  * @private
130  * @param value
131  * @returns {Boolean}
132  */
133 jasmine.isNumber_ = function(value) {
134   return jasmine.isA_("Number", value);
135 };
136 
137 /**
138  * @ignore
139  * @private
140  * @param {String} typeName
141  * @param value
142  * @returns {Boolean}
143  */
144 jasmine.isA_ = function(typeName, value) {
145   return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';
146 };
147 
148 /**
149  * Pretty printer for expecations.  Takes any object and turns it into a human-readable string.
150  *
151  * @param value {Object} an object to be outputted
152  * @returns {String}
153  */
154 jasmine.pp = function(value) {
155   var stringPrettyPrinter = new jasmine.StringPrettyPrinter();
156   stringPrettyPrinter.format(value);
157   return stringPrettyPrinter.string;
158 };
159 
160 /**
161  * Returns true if the object is a DOM Node.
162  *
163  * @param {Object} obj object to check
164  * @returns {Boolean}
165  */
166 jasmine.isDomNode = function(obj) {
167   return obj['nodeType'] > 0;
168 };
169 
170 /**
171  * Returns a matchable 'generic' object of the class type.  For use in expecations of type when values don't matter.
172  *
173  * @example
174  * // don't care about which function is passed in, as long as it's a function
175  * expect(mySpy).wasCalledWith(jasmine.any(Function));
176  *
177  * @param {Class} clazz
178  * @returns matchable object of the type clazz
179  */
180 jasmine.any = function(clazz) {
181   return new jasmine.Matchers.Any(clazz);
182 };
183 
184 /**
185  * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
186  *
187  * Spies should be created in test setup, before expectations.  They can then be checked, using the standard Jasmine
188  * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
189  *
190  * A Spy has the following mehtod: wasCalled, callCount, mostRecentCall, and argsForCall (see docs)
191  * Spies are torn down at the end of every spec.
192  *
193  * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
194  *
195  * @example
196  * // a stub
197  * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
198  *
199  * // spy example
200  * var foo = {
201  *   not: function(bool) { return !bool; }
202  * }
203  *
204  * // actual foo.not will not be called, execution stops
205  * spyOn(foo, 'not');
206 
207  // foo.not spied upon, execution will continue to implementation
208  * spyOn(foo, 'not').andCallThrough();
209  *
210  * // fake example
211  * var foo = {
212  *   not: function(bool) { return !bool; }
213  * }
214  *
215  * // foo.not(val) will return val
216  * spyOn(foo, 'not').andCallFake(function(value) {return value;});
217  *
218  * // mock example
219  * foo.not(7 == 7);
220  * expect(foo.not).wasCalled();
221  * expect(foo.not).wasCalledWith(true);
222  *
223  * @constructor
224  * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
225  * @param {String} name
226  */
227 jasmine.Spy = function(name) {
228   /**
229    * The name of the spy, if provided.
230    */
231   this.identity = name || 'unknown';
232   /**
233    *  Is this Object a spy?
234    */
235   this.isSpy = true;
236   /**
237    * The actual function this spy stubs.
238    */
239   this.plan = function() {
240   };
241   /**
242    * Tracking of the most recent call to the spy.
243    * @example
244    * var mySpy = jasmine.createSpy('foo');
245    * mySpy(1, 2);
246    * mySpy.mostRecentCall.args = [1, 2];
247    */
248   this.mostRecentCall = {};
249 
250   /**
251    * Holds arguments for each call to the spy, indexed by call count
252    * @example
253    * var mySpy = jasmine.createSpy('foo');
254    * mySpy(1, 2);
255    * mySpy(7, 8);
256    * mySpy.mostRecentCall.args = [7, 8];
257    * mySpy.argsForCall[0] = [1, 2];
258    * mySpy.argsForCall[1] = [7, 8];
259    */
260   this.argsForCall = [];
261   this.calls = [];
262 };
263 
264 /**
265  * Tells a spy to call through to the actual implemenatation.
266  *
267  * @example
268  * var foo = {
269  *   bar: function() { // do some stuff }
270  * }
271  *
272  * // defining a spy on an existing property: foo.bar
273  * spyOn(foo, 'bar').andCallThrough();
274  */
275 jasmine.Spy.prototype.andCallThrough = function() {
276   this.plan = this.originalValue;
277   return this;
278 };
279 
280 /**
281  * For setting the return value of a spy.
282  *
283  * @example
284  * // defining a spy from scratch: foo() returns 'baz'
285  * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
286  *
287  * // defining a spy on an existing property: foo.bar() returns 'baz'
288  * spyOn(foo, 'bar').andReturn('baz');
289  *
290  * @param {Object} value
291  */
292 jasmine.Spy.prototype.andReturn = function(value) {
293   this.plan = function() {
294     return value;
295   };
296   return this;
297 };
298 
299 /**
300  * For throwing an exception when a spy is called.
301  *
302  * @example
303  * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
304  * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
305  *
306  * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
307  * spyOn(foo, 'bar').andThrow('baz');
308  *
309  * @param {String} exceptionMsg
310  */
311 jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
312   this.plan = function() {
313     throw exceptionMsg;
314   };
315   return this;
316 };
317 
318 /**
319  * Calls an alternate implementation when a spy is called.
320  *
321  * @example
322  * var baz = function() {
323  *   // do some stuff, return something
324  * }
325  * // defining a spy from scratch: foo() calls the function baz
326  * var foo = jasmine.createSpy('spy on foo').andCall(baz);
327  *
328  * // defining a spy on an existing property: foo.bar() calls an anonymnous function
329  * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
330  *
331  * @param {Function} fakeFunc
332  */
333 jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
334   this.plan = fakeFunc;
335   return this;
336 };
337 
338 /**
339  * Resets all of a spy's the tracking variables so that it can be used again.
340  *
341  * @example
342  * spyOn(foo, 'bar');
343  *
344  * foo.bar();
345  *
346  * expect(foo.bar.callCount).toEqual(1);
347  *
348  * foo.bar.reset();
349  *
350  * expect(foo.bar.callCount).toEqual(0);
351  */
352 jasmine.Spy.prototype.reset = function() {
353   this.wasCalled = false;
354   this.callCount = 0;
355   this.argsForCall = [];
356   this.calls = [];
357   this.mostRecentCall = {};
358 };
359 
360 jasmine.createSpy = function(name) {
361 
362   var spyObj = function() {
363     spyObj.wasCalled = true;
364     spyObj.callCount++;
365     var args = jasmine.util.argsToArray(arguments);
366     spyObj.mostRecentCall.object = this;
367     spyObj.mostRecentCall.args = args;
368     spyObj.argsForCall.push(args);
369     spyObj.calls.push({object: this, args: args});
370     return spyObj.plan.apply(this, arguments);
371   };
372 
373   var spy = new jasmine.Spy(name);
374 
375   for (var prop in spy) {
376     spyObj[prop] = spy[prop];
377   }
378 
379   spyObj.reset();
380 
381   return spyObj;
382 };
383 
384 /**
385  * Determines whether an object is a spy.
386  *
387  * @param {jasmine.Spy|Object} putativeSpy
388  * @returns {Boolean}
389  */
390 jasmine.isSpy = function(putativeSpy) {
391   return putativeSpy && putativeSpy.isSpy;
392 };
393 
394 /**
395  * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
396  * large in one call.
397  *
398  * @param {String} baseName name of spy class
399  * @param {Array} methodNames array of names of methods to make spies
400  */
401 jasmine.createSpyObj = function(baseName, methodNames) {
402   if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
403     throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
404   }
405   var obj = {};
406   for (var i = 0; i < methodNames.length; i++) {
407     obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
408   }
409   return obj;
410 };
411 
412 jasmine.log = function() {
413   var spec = jasmine.getEnv().currentSpec;
414   spec.log.apply(spec, arguments);
415 };
416 
417 /**
418  * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
419  *
420  * @example
421  * // spy example
422  * var foo = {
423  *   not: function(bool) { return !bool; }
424  * }
425  * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
426  *
427  * @see jasmine.createSpy
428  * @param obj
429  * @param methodName
430  * @returns a Jasmine spy that can be chained with all spy methods
431  */
432 var spyOn = function(obj, methodName) {
433   return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
434 };
435 
436 /**
437  * Creates a Jasmine spec that will be added to the current suite.
438  *
439  * // TODO: pending tests
440  *
441  * @example
442  * it('should be true', function() {
443  *   expect(true).toEqual(true);
444  * });
445  *
446  * @param {String} desc description of this specification
447  * @param {Function} func defines the preconditions and expectations of the spec
448  */
449 var it = function(desc, func) {
450   return jasmine.getEnv().it(desc, func);
451 };
452 
453 /**
454  * Creates a <em>disabled</em> Jasmine spec.
455  *
456  * A convenience method that allows existing specs to be disabled temporarily during development.
457  *
458  * @param {String} desc description of this specification
459  * @param {Function} func defines the preconditions and expectations of the spec
460  */
461 var xit = function(desc, func) {
462   return jasmine.getEnv().xit(desc, func);
463 };
464 
465 /**
466  * Starts a chain for a Jasmine expectation.
467  *
468  * It is passed an Object that is the actual value and should chain to one of the many
469  * jasmine.Matchers functions.
470  *
471  * @param {Object} actual Actual value to test against and expected value
472  */
473 var expect = function(actual) {
474   return jasmine.getEnv().currentSpec.expect(actual);
475 };
476 
477 /**
478  * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
479  *
480  * @param {Function} func Function that defines part of a jasmine spec.
481  */
482 var runs = function(func) {
483   jasmine.getEnv().currentSpec.runs(func);
484 };
485 
486 /**
487  * Waits for a timeout before moving to the next runs()-defined block.
488  * @param {Number} timeout
489  */
490 var waits = function(timeout) {
491   jasmine.getEnv().currentSpec.waits(timeout);
492 };
493 
494 /**
495  * Waits for the latchFunction to return true before proceeding to the next runs()-defined block.
496  *
497  * @param {Number} timeout
498  * @param {Function} latchFunction
499  * @param {String} message
500  */
501 var waitsFor = function(timeout, latchFunction, message) {
502   jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message);
503 };
504 
505 /**
506  * A function that is called before each spec in a suite.
507  *
508  * Used for spec setup, including validating assumptions.
509  *
510  * @param {Function} beforeEachFunction
511  */
512 var beforeEach = function(beforeEachFunction) {
513   jasmine.getEnv().beforeEach(beforeEachFunction);
514 };
515 
516 /**
517  * A function that is called after each spec in a suite.
518  *
519  * Used for restoring any state that is hijacked during spec execution.
520  *
521  * @param {Function} afterEachFunction
522  */
523 var afterEach = function(afterEachFunction) {
524   jasmine.getEnv().afterEach(afterEachFunction);
525 };
526 
527 /**
528  * Defines a suite of specifications.
529  *
530  * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
531  * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
532  * of setup in some tests.
533  *
534  * @example
535  * // TODO: a simple suite
536  *
537  * // TODO: a simple suite with a nested describe block
538  *
539  * @param {String} description A string, usually the class under test.
540  * @param {Function} specDefinitions function that defines several specs.
541  */
542 var describe = function(description, specDefinitions) {
543   return jasmine.getEnv().describe(description, specDefinitions);
544 };
545 
546 /**
547  * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
548  *
549  * @param {String} description A string, usually the class under test.
550  * @param {Function} specDefinitions function that defines several specs.
551  */
552 var xdescribe = function(description, specDefinitions) {
553   return jasmine.getEnv().xdescribe(description, specDefinitions);
554 };
555 
556 
557 // Provide the XMLHttpRequest class for IE 5.x-6.x:
558 jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
559   try {
560     return new ActiveXObject("Msxml2.XMLHTTP.6.0");
561   } catch(e) {
562   }
563   try {
564     return new ActiveXObject("Msxml2.XMLHTTP.3.0");
565   } catch(e) {
566   }
567   try {
568     return new ActiveXObject("Msxml2.XMLHTTP");
569   } catch(e) {
570   }
571   try {
572     return new ActiveXObject("Microsoft.XMLHTTP");
573   } catch(e) {
574   }
575   throw new Error("This browser does not support XMLHttpRequest.");
576 } : XMLHttpRequest;
577