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 jasmine.getGlobal()[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).toHaveBeenCalledWith(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 fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
191  *
192  * Spies are torn down at the end of every spec.
193  *
194  * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
195  *
196  * @example
197  * // a stub
198  * var myStub = jasmine.createSpy('myStub');  // can be used anywhere
199  *
200  * // spy example
201  * var foo = {
202  *   not: function(bool) { return !bool; }
203  * }
204  *
205  * // actual foo.not will not be called, execution stops
206  * spyOn(foo, 'not');
207 
208  // foo.not spied upon, execution will continue to implementation
209  * spyOn(foo, 'not').andCallThrough();
210  *
211  * // fake example
212  * var foo = {
213  *   not: function(bool) { return !bool; }
214  * }
215  *
216  * // foo.not(val) will return val
217  * spyOn(foo, 'not').andCallFake(function(value) {return value;});
218  *
219  * // mock example
220  * foo.not(7 == 7);
221  * expect(foo.not).toHaveBeenCalled();
222  * expect(foo.not).toHaveBeenCalledWith(true);
223  *
224  * @constructor
225  * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
226  * @param {String} name
227  */
228 jasmine.Spy = function(name) {
229   /**
230    * The name of the spy, if provided.
231    */
232   this.identity = name || 'unknown';
233   /**
234    *  Is this Object a spy?
235    */
236   this.isSpy = true;
237   /**
238    * The actual function this spy stubs.
239    */
240   this.plan = function() {
241   };
242   /**
243    * Tracking of the most recent call to the spy.
244    * @example
245    * var mySpy = jasmine.createSpy('foo');
246    * mySpy(1, 2);
247    * mySpy.mostRecentCall.args = [1, 2];
248    */
249   this.mostRecentCall = {};
250 
251   /**
252    * Holds arguments for each call to the spy, indexed by call count
253    * @example
254    * var mySpy = jasmine.createSpy('foo');
255    * mySpy(1, 2);
256    * mySpy(7, 8);
257    * mySpy.mostRecentCall.args = [7, 8];
258    * mySpy.argsForCall[0] = [1, 2];
259    * mySpy.argsForCall[1] = [7, 8];
260    */
261   this.argsForCall = [];
262   this.calls = [];
263 };
264 
265 /**
266  * Tells a spy to call through to the actual implemenatation.
267  *
268  * @example
269  * var foo = {
270  *   bar: function() { // do some stuff }
271  * }
272  *
273  * // defining a spy on an existing property: foo.bar
274  * spyOn(foo, 'bar').andCallThrough();
275  */
276 jasmine.Spy.prototype.andCallThrough = function() {
277   this.plan = this.originalValue;
278   return this;
279 };
280 
281 /**
282  * For setting the return value of a spy.
283  *
284  * @example
285  * // defining a spy from scratch: foo() returns 'baz'
286  * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
287  *
288  * // defining a spy on an existing property: foo.bar() returns 'baz'
289  * spyOn(foo, 'bar').andReturn('baz');
290  *
291  * @param {Object} value
292  */
293 jasmine.Spy.prototype.andReturn = function(value) {
294   this.plan = function() {
295     return value;
296   };
297   return this;
298 };
299 
300 /**
301  * For throwing an exception when a spy is called.
302  *
303  * @example
304  * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
305  * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
306  *
307  * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
308  * spyOn(foo, 'bar').andThrow('baz');
309  *
310  * @param {String} exceptionMsg
311  */
312 jasmine.Spy.prototype.andThrow = function(exceptionMsg) {
313   this.plan = function() {
314     throw exceptionMsg;
315   };
316   return this;
317 };
318 
319 /**
320  * Calls an alternate implementation when a spy is called.
321  *
322  * @example
323  * var baz = function() {
324  *   // do some stuff, return something
325  * }
326  * // defining a spy from scratch: foo() calls the function baz
327  * var foo = jasmine.createSpy('spy on foo').andCall(baz);
328  *
329  * // defining a spy on an existing property: foo.bar() calls an anonymnous function
330  * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
331  *
332  * @param {Function} fakeFunc
333  */
334 jasmine.Spy.prototype.andCallFake = function(fakeFunc) {
335   this.plan = fakeFunc;
336   return this;
337 };
338 
339 /**
340  * Resets all of a spy's the tracking variables so that it can be used again.
341  *
342  * @example
343  * spyOn(foo, 'bar');
344  *
345  * foo.bar();
346  *
347  * expect(foo.bar.callCount).toEqual(1);
348  *
349  * foo.bar.reset();
350  *
351  * expect(foo.bar.callCount).toEqual(0);
352  */
353 jasmine.Spy.prototype.reset = function() {
354   this.wasCalled = false;
355   this.callCount = 0;
356   this.argsForCall = [];
357   this.calls = [];
358   this.mostRecentCall = {};
359 };
360 
361 jasmine.createSpy = function(name) {
362 
363   var spyObj = function() {
364     spyObj.wasCalled = true;
365     spyObj.callCount++;
366     var args = jasmine.util.argsToArray(arguments);
367     spyObj.mostRecentCall.object = this;
368     spyObj.mostRecentCall.args = args;
369     spyObj.argsForCall.push(args);
370     spyObj.calls.push({object: this, args: args});
371     return spyObj.plan.apply(this, arguments);
372   };
373 
374   var spy = new jasmine.Spy(name);
375 
376   for (var prop in spy) {
377     spyObj[prop] = spy[prop];
378   }
379 
380   spyObj.reset();
381 
382   return spyObj;
383 };
384 
385 /**
386  * Determines whether an object is a spy.
387  *
388  * @param {jasmine.Spy|Object} putativeSpy
389  * @returns {Boolean}
390  */
391 jasmine.isSpy = function(putativeSpy) {
392   return putativeSpy && putativeSpy.isSpy;
393 };
394 
395 /**
396  * Creates a more complicated spy: an Object that has every property a function that is a spy.  Used for stubbing something
397  * large in one call.
398  *
399  * @param {String} baseName name of spy class
400  * @param {Array} methodNames array of names of methods to make spies
401  */
402 jasmine.createSpyObj = function(baseName, methodNames) {
403   if (!jasmine.isArray_(methodNames) || methodNames.length == 0) {
404     throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
405   }
406   var obj = {};
407   for (var i = 0; i < methodNames.length; i++) {
408     obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);
409   }
410   return obj;
411 };
412 
413 /**
414  * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
415  *
416  * Be careful not to leave calls to <code>jasmine.log</code> in production code.
417  */
418 jasmine.log = function() {
419   var spec = jasmine.getEnv().currentSpec;
420   spec.log.apply(spec, arguments);
421 };
422 
423 /**
424  * Function that installs a spy on an existing object's method name.  Used within a Spec to create a spy.
425  *
426  * @example
427  * // spy example
428  * var foo = {
429  *   not: function(bool) { return !bool; }
430  * }
431  * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
432  *
433  * @see jasmine.createSpy
434  * @param obj
435  * @param methodName
436  * @returns a Jasmine spy that can be chained with all spy methods
437  */
438 var spyOn = function(obj, methodName) {
439   return jasmine.getEnv().currentSpec.spyOn(obj, methodName);
440 };
441 
442 /**
443  * Creates a Jasmine spec that will be added to the current suite.
444  *
445  * // TODO: pending tests
446  *
447  * @example
448  * it('should be true', function() {
449  *   expect(true).toEqual(true);
450  * });
451  *
452  * @param {String} desc description of this specification
453  * @param {Function} func defines the preconditions and expectations of the spec
454  */
455 var it = function(desc, func) {
456   return jasmine.getEnv().it(desc, func);
457 };
458 
459 /**
460  * Creates a <em>disabled</em> Jasmine spec.
461  *
462  * A convenience method that allows existing specs to be disabled temporarily during development.
463  *
464  * @param {String} desc description of this specification
465  * @param {Function} func defines the preconditions and expectations of the spec
466  */
467 var xit = function(desc, func) {
468   return jasmine.getEnv().xit(desc, func);
469 };
470 
471 /**
472  * Starts a chain for a Jasmine expectation.
473  *
474  * It is passed an Object that is the actual value and should chain to one of the many
475  * jasmine.Matchers functions.
476  *
477  * @param {Object} actual Actual value to test against and expected value
478  */
479 var expect = function(actual) {
480   return jasmine.getEnv().currentSpec.expect(actual);
481 };
482 
483 /**
484  * Defines part of a jasmine spec.  Used in cominbination with waits or waitsFor in asynchrnous specs.
485  *
486  * @param {Function} func Function that defines part of a jasmine spec.
487  */
488 var runs = function(func) {
489   jasmine.getEnv().currentSpec.runs(func);
490 };
491 
492 /**
493  * Waits for a timeout before moving to the next runs()-defined block.
494  * @param {Number} timeout
495  */
496 var waits = function(timeout) {
497   jasmine.getEnv().currentSpec.waits(timeout);
498 };
499 
500 /**
501  * Waits for the latchFunction to return true before proceeding to the next runs()-defined block.
502  *
503  * @param {Number} timeout
504  * @param {Function} latchFunction
505  * @param {String} message
506  */
507 var waitsFor = function(timeout, latchFunction, message) {
508   jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message);
509 };
510 
511 /**
512  * A function that is called before each spec in a suite.
513  *
514  * Used for spec setup, including validating assumptions.
515  *
516  * @param {Function} beforeEachFunction
517  */
518 var beforeEach = function(beforeEachFunction) {
519   jasmine.getEnv().beforeEach(beforeEachFunction);
520 };
521 
522 /**
523  * A function that is called after each spec in a suite.
524  *
525  * Used for restoring any state that is hijacked during spec execution.
526  *
527  * @param {Function} afterEachFunction
528  */
529 var afterEach = function(afterEachFunction) {
530   jasmine.getEnv().afterEach(afterEachFunction);
531 };
532 
533 /**
534  * Defines a suite of specifications.
535  *
536  * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
537  * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
538  * of setup in some tests.
539  *
540  * @example
541  * // TODO: a simple suite
542  *
543  * // TODO: a simple suite with a nested describe block
544  *
545  * @param {String} description A string, usually the class under test.
546  * @param {Function} specDefinitions function that defines several specs.
547  */
548 var describe = function(description, specDefinitions) {
549   return jasmine.getEnv().describe(description, specDefinitions);
550 };
551 
552 /**
553  * Disables a suite of specifications.  Used to disable some suites in a file, or files, temporarily during development.
554  *
555  * @param {String} description A string, usually the class under test.
556  * @param {Function} specDefinitions function that defines several specs.
557  */
558 var xdescribe = function(description, specDefinitions) {
559   return jasmine.getEnv().xdescribe(description, specDefinitions);
560 };
561 
562 
563 // Provide the XMLHttpRequest class for IE 5.x-6.x:
564 jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {
565   try {
566     return new ActiveXObject("Msxml2.XMLHTTP.6.0");
567   } catch(e) {
568   }
569   try {
570     return new ActiveXObject("Msxml2.XMLHTTP.3.0");
571   } catch(e) {
572   }
573   try {
574     return new ActiveXObject("Msxml2.XMLHTTP");
575   } catch(e) {
576   }
577   try {
578     return new ActiveXObject("Microsoft.XMLHTTP");
579   } catch(e) {
580   }
581   throw new Error("This browser does not support XMLHttpRequest.");
582 } : XMLHttpRequest;
583