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