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 * Large or small values here may result in slow test running & "Too much recursion" errors 17 * 18 */ 19 jasmine.UPDATE_INTERVAL = 250; 20 21 /** 22 * Allows for bound functions to be comapred. Internal use only. 23 * 24 * @ignore 25 * @private 26 * @param base {Object} bound 'this' for the function 27 * @param name {Function} function to find 28 */ 29 jasmine.bindOriginal_ = function(base, name) { 30 var original = base[name]; 31 return function() { 32 if (original.apply) { 33 return original.apply(base, arguments); 34 } else { 35 //IE support 36 if (base == window) { 37 return window[name].apply(window, arguments); 38 } 39 } 40 }; 41 }; 42 43 jasmine.setTimeout = jasmine.bindOriginal_(window, 'setTimeout'); 44 jasmine.clearTimeout = jasmine.bindOriginal_(window, 'clearTimeout'); 45 jasmine.setInterval = jasmine.bindOriginal_(window, 'setInterval'); 46 jasmine.clearInterval = jasmine.bindOriginal_(window, 'clearInterval'); 47 48 jasmine.MessageResult = function(text) { 49 this.type = 'MessageResult'; 50 this.text = text; 51 this.trace = new Error(); // todo: test better 52 }; 53 54 jasmine.ExpectationResult = function(passed, message, details) { 55 this.type = 'ExpectationResult'; 56 this.passed_ = passed; 57 this.message = message; 58 this.details = details; 59 this.trace = new Error(message); // todo: test better 60 }; 61 62 jasmine.ExpectationResult.prototype.passed = function () { 63 return this.passed_; 64 }; 65 66 /** 67 * Getter for the Jasmine environment. Ensures one gets created 68 */ 69 jasmine.getEnv = function() { 70 return jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); 71 }; 72 73 /** 74 * @ignore 75 * @private 76 * @param value 77 * @returns {Boolean} 78 */ 79 jasmine.isArray_ = function(value) { 80 return value && 81 typeof value === 'object' && 82 typeof value.length === 'number' && 83 typeof value.splice === 'function' && 84 !(value.propertyIsEnumerable('length')); 85 }; 86 87 /** 88 * Pretty printer for expecations. Takes any object and turns it into a human-readable string. 89 * 90 * @param value {Object} an object to be outputted 91 * @returns {String} 92 */ 93 jasmine.pp = function(value) { 94 var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); 95 stringPrettyPrinter.format(value); 96 return stringPrettyPrinter.string; 97 }; 98 99 /** 100 * Returns true if the object is a DOM Node. 101 * 102 * @param {Object} obj object to check 103 * @returns {Boolean} 104 */ 105 jasmine.isDomNode = function(obj) { 106 return obj['nodeType'] > 0; 107 }; 108 109 /** 110 * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. 111 * 112 * @example 113 * // don't care about which function is passed in, as long as it's a function 114 * expect(mySpy).wasCalledWith(jasmine.any(Function)); 115 * 116 * @param {Class} clazz 117 * @returns matchable object of the type clazz 118 */ 119 jasmine.any = function(clazz) { 120 return new jasmine.Matchers.Any(clazz); 121 }; 122 123 /** 124 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. 125 * 126 * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine 127 * expectation syntax. Spies can be checked if they were called or not and what the calling params were. 128 * 129 * A Spy has the following mehtod: wasCalled, callCount, mostRecentCall, and argsForCall (see docs) 130 * Spies are torn down at the end of every spec. 131 * 132 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. 133 * 134 * @example 135 * // a stub 136 * var myStub = jasmine.createSpy('myStub'); // can be used anywhere 137 * 138 * // spy example 139 * var foo = { 140 * not: function(bool) { return !bool; } 141 * } 142 * 143 * // actual foo.not will not be called, execution stops 144 * spyOn(foo, 'not'); 145 146 // foo.not spied upon, execution will continue to implementation 147 * spyOn(foo, 'not').andCallThrough(); 148 * 149 * // fake example 150 * var foo = { 151 * not: function(bool) { return !bool; } 152 * } 153 * 154 * // foo.not(val) will return val 155 * spyOn(foo, 'not').andCallFake(function(value) {return value;}); 156 * 157 * // mock example 158 * foo.not(7 == 7); 159 * expect(foo.not).wasCalled(); 160 * expect(foo.not).wasCalledWith(true); 161 * 162 * @constructor 163 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj 164 * @param {String} name 165 */ 166 jasmine.Spy = function(name) { 167 /** 168 * The name of the spy, if provided. 169 */ 170 this.identity = name || 'unknown'; 171 /** 172 * Is this Object a spy? 173 */ 174 this.isSpy = true; 175 /** 176 * The acutal function this spy stubs. 177 */ 178 this.plan = function() { 179 }; 180 /** 181 * Tracking of the most recent call to the spy. 182 * @example 183 * var mySpy = jasmine.createSpy('foo'); 184 * mySpy(1, 2); 185 * mySpy.mostRecentCall.args = [1, 2]; 186 */ 187 this.mostRecentCall = {}; 188 189 /** 190 * Holds arguments for each call to the spy, indexed by call count 191 * @example 192 * var mySpy = jasmine.createSpy('foo'); 193 * mySpy(1, 2); 194 * mySpy(7, 8); 195 * mySpy.mostRecentCall.args = [7, 8]; 196 * mySpy.argsForCall[0] = [1, 2]; 197 * mySpy.argsForCall[1] = [7, 8]; 198 */ 199 this.argsForCall = []; 200 this.calls = []; 201 }; 202 203 /** 204 * Tells a spy to call through to the actual implemenatation. 205 * 206 * @example 207 * var foo = { 208 * bar: function() { // do some stuff } 209 * } 210 * 211 * // defining a spy on an existing property: foo.bar 212 * spyOn(foo, 'bar').andCallThrough(); 213 */ 214 jasmine.Spy.prototype.andCallThrough = function() { 215 this.plan = this.originalValue; 216 return this; 217 }; 218 219 /** 220 * For setting the return value of a spy. 221 * 222 * @example 223 * // defining a spy from scratch: foo() returns 'baz' 224 * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); 225 * 226 * // defining a spy on an existing property: foo.bar() returns 'baz' 227 * spyOn(foo, 'bar').andReturn('baz'); 228 * 229 * @param {Object} value 230 */ 231 jasmine.Spy.prototype.andReturn = function(value) { 232 this.plan = function() { 233 return value; 234 }; 235 return this; 236 }; 237 238 /** 239 * For throwing an exception when a spy is called. 240 * 241 * @example 242 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' 243 * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); 244 * 245 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' 246 * spyOn(foo, 'bar').andThrow('baz'); 247 * 248 * @param {String} exceptionMsg 249 */ 250 jasmine.Spy.prototype.andThrow = function(exceptionMsg) { 251 this.plan = function() { 252 throw exceptionMsg; 253 }; 254 return this; 255 }; 256 257 /** 258 * Calls an alternate implementation when a spy is called. 259 * 260 * @example 261 * var baz = function() { 262 * // do some stuff, return something 263 * } 264 * // defining a spy from scratch: foo() calls the function baz 265 * var foo = jasmine.createSpy('spy on foo').andCall(baz); 266 * 267 * // defining a spy on an existing property: foo.bar() calls an anonymnous function 268 * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); 269 * 270 * @param {Function} fakeFunc 271 */ 272 jasmine.Spy.prototype.andCallFake = function(fakeFunc) { 273 this.plan = fakeFunc; 274 return this; 275 }; 276 277 /** 278 * Resets all of a spy's the tracking variables so that it can be used again. 279 * 280 * @example 281 * spyOn(foo, 'bar'); 282 * 283 * foo.bar(); 284 * 285 * expect(foo.bar.callCount).toEqual(1); 286 * 287 * foo.bar.reset(); 288 * 289 * expect(foo.bar.callCount).toEqual(0); 290 */ 291 jasmine.Spy.prototype.reset = function() { 292 this.wasCalled = false; 293 this.callCount = 0; 294 this.argsForCall = []; 295 this.calls = []; 296 this.mostRecentCall = {}; 297 }; 298 299 jasmine.createSpy = function(name) { 300 301 var spyObj = function() { 302 spyObj.wasCalled = true; 303 spyObj.callCount++; 304 var args = jasmine.util.argsToArray(arguments); 305 spyObj.mostRecentCall.object = this; 306 spyObj.mostRecentCall.args = args; 307 spyObj.argsForCall.push(args); 308 spyObj.calls.push({object: this, args: args}); 309 return spyObj.plan.apply(this, arguments); 310 }; 311 312 var spy = new jasmine.Spy(name); 313 314 for (var prop in spy) { 315 spyObj[prop] = spy[prop]; 316 } 317 318 spyObj.reset(); 319 320 return spyObj; 321 }; 322 323 /** 324 * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something 325 * large in one call. 326 * 327 * @param {String} baseName name of spy class 328 * @param {Array} methodNames array of names of methods to make spies 329 */ 330 jasmine.createSpyObj = function(baseName, methodNames) { 331 var obj = {}; 332 for (var i = 0; i < methodNames.length; i++) { 333 obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); 334 } 335 return obj; 336 }; 337 338 jasmine.log = function(message) { 339 jasmine.getEnv().currentSpec.log(message); 340 }; 341 342 /** 343 * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. 344 * 345 * @example 346 * // spy example 347 * var foo = { 348 * not: function(bool) { return !bool; } 349 * } 350 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops 351 * 352 * @see jasmine.createSpy 353 * @param obj 354 * @param methodName 355 * @returns a Jasmine spy that can be chained with all spy methods 356 */ 357 var spyOn = function(obj, methodName) { 358 return jasmine.getEnv().currentSpec.spyOn(obj, methodName); 359 }; 360 361 /** 362 * Creates a Jasmine spec that will be added to the current suite. 363 * 364 * // TODO: pending tests 365 * 366 * @example 367 * it('should be true', function() { 368 * expect(true).toEqual(true); 369 * }); 370 * 371 * @param {String} desc description of this specification 372 * @param {Function} func defines the preconditions and expectations of the spec 373 */ 374 var it = function(desc, func) { 375 return jasmine.getEnv().it(desc, func); 376 }; 377 378 /** 379 * Creates a <em>disabled</em> Jasmine spec. 380 * 381 * A convenience method that allows existing specs to be disabled temporarily during development. 382 * 383 * @param {String} desc description of this specification 384 * @param {Function} func defines the preconditions and expectations of the spec 385 */ 386 var xit = function(desc, func) { 387 return jasmine.getEnv().xit(desc, func); 388 }; 389 390 /** 391 * Starts a chain for a Jasmine expectation. 392 * 393 * It is passed an Object that is the actual value and should chain to one of the many 394 * jasmine.Matchers functions. 395 * 396 * @param {Object} actual Actual value to test against and expected value 397 */ 398 var expect = function(actual) { 399 return jasmine.getEnv().currentSpec.expect(actual); 400 }; 401 402 /** 403 * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. 404 * 405 * @param {Function} func Function that defines part of a jasmine spec. 406 */ 407 var runs = function(func) { 408 jasmine.getEnv().currentSpec.runs(func); 409 }; 410 411 /** 412 * Waits for a timeout before moving to the next runs()-defined block. 413 * @param {Number} timeout 414 */ 415 var waits = function(timeout) { 416 jasmine.getEnv().currentSpec.waits(timeout); 417 }; 418 419 /** 420 * Waits for the latchFunction to return true before proceeding to the next runs()-defined block. 421 * 422 * @param {Number} timeout 423 * @param {Function} latchFunction 424 * @param {String} message 425 */ 426 var waitsFor = function(timeout, latchFunction, message) { 427 jasmine.getEnv().currentSpec.waitsFor(timeout, latchFunction, message); 428 }; 429 430 /** 431 * A function that is called before each spec in a suite. 432 * 433 * Used for spec setup, including validating assumptions. 434 * 435 * @param {Function} beforeEachFunction 436 */ 437 var beforeEach = function(beforeEachFunction) { 438 jasmine.getEnv().beforeEach(beforeEachFunction); 439 }; 440 441 /** 442 * A function that is called after each spec in a suite. 443 * 444 * Used for restoring any state that is hijacked during spec execution. 445 * 446 * @param {Function} afterEachFunction 447 */ 448 var afterEach = function(afterEachFunction) { 449 jasmine.getEnv().afterEach(afterEachFunction); 450 }; 451 452 /** 453 * Defines a suite of specifications. 454 * 455 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared 456 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization 457 * of setup in some tests. 458 * 459 * @example 460 * // TODO: a simple suite 461 * 462 * // TODO: a simple suite with a nested describe block 463 * 464 * @param {String} description A string, usually the class under test. 465 * @param {Function} specDefinitions function that defines several specs. 466 */ 467 var describe = function(description, specDefinitions) { 468 return jasmine.getEnv().describe(description, specDefinitions); 469 }; 470 471 /** 472 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. 473 * 474 * @param {String} description A string, usually the class under test. 475 * @param {Function} specDefinitions function that defines several specs. 476 */ 477 var xdescribe = function(description, specDefinitions) { 478 return jasmine.getEnv().xdescribe(description, specDefinitions); 479 }; 480 481 482 jasmine.XmlHttpRequest = XMLHttpRequest; 483 484 // Provide the XMLHttpRequest class for IE 5.x-6.x: 485 if (typeof XMLHttpRequest == "undefined") jasmine.XmlHttpRequest = function() { 486 try { 487 return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 488 } catch(e) { 489 } 490 try { 491 return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 492 } catch(e) { 493 } 494 try { 495 return new ActiveXObject("Msxml2.XMLHTTP"); 496 } catch(e) { 497 } 498 try { 499 return new ActiveXObject("Microsoft.XMLHTTP"); 500 } catch(e) { 501 } 502 throw new Error("This browser does not support XMLHttpRequest."); 503 }; 504 505 /** 506 * Adds suite files to an HTML document so that they are executed, thus adding them to the current 507 * Jasmine environment. 508 * 509 * @param {String} url path to the file to include 510 * @param {Boolean} opt_global 511 */ 512 jasmine.include = function(url, opt_global) { 513 if (opt_global) { 514 document.write('<script type="text/javascript" src="' + url + '"></' + 'script>'); 515 } else { 516 var xhr; 517 try { 518 xhr = new jasmine.XmlHttpRequest(); 519 xhr.open("GET", url, false); 520 xhr.send(null); 521 } catch(e) { 522 throw new Error("couldn't fetch " + url + ": " + e); 523 } 524 525 return eval(xhr.responseText); 526 } 527 }; 528 529 jasmine.version_= { 530 "major": 0, 531 "minor": 10, 532 "build": 0, 533 "revision": 1256142331 534 }; 535 /** 536 * @namespace 537 */ 538 jasmine.util = {}; 539 540 /** 541 * Declare that a child class inherite it's prototype from the parent class. 542 * 543 * @private 544 * @param {Function} childClass 545 * @param {Function} parentClass 546 */ 547 jasmine.util.inherit = function(childClass, parentClass) { 548 var subclass = function() { 549 }; 550 subclass.prototype = parentClass.prototype; 551 childClass.prototype = new subclass; 552 }; 553 554 jasmine.util.formatException = function(e) { 555 var lineNumber; 556 if (e.line) { 557 lineNumber = e.line; 558 } 559 else if (e.lineNumber) { 560 lineNumber = e.lineNumber; 561 } 562 563 var file; 564 565 if (e.sourceURL) { 566 file = e.sourceURL; 567 } 568 else if (e.fileName) { 569 file = e.fileName; 570 } 571 572 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); 573 574 if (file && lineNumber) { 575 message += ' in ' + file + ' (line ' + lineNumber + ')'; 576 } 577 578 return message; 579 }; 580 581 jasmine.util.htmlEscape = function(str) { 582 if (!str) return str; 583 return str.replace(/&/g, '&') 584 .replace(/</g, '<') 585 .replace(/>/g, '>'); 586 }; 587 588 jasmine.util.argsToArray = function(args) { 589 var arrayOfArgs = []; 590 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); 591 return arrayOfArgs; 592 }; 593 594 /** 595 * Environment for Jasmine 596 * 597 * @constructor 598 */ 599 jasmine.Env = function() { 600 this.currentSpec = null; 601 this.currentSuite = null; 602 this.currentRunner_ = new jasmine.Runner(this); 603 this.currentlyRunningTests = false; 604 605 this.reporter = new jasmine.MultiReporter(); 606 607 this.updateInterval = jasmine.UPDATE_INTERVAL 608 this.lastUpdate = 0; 609 this.specFilter = function() { 610 return true; 611 }; 612 613 this.nextSpecId_ = 0; 614 this.nextSuiteId_ = 0; 615 this.equalityTesters_ = []; 616 }; 617 618 619 jasmine.Env.prototype.setTimeout = jasmine.setTimeout; 620 jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; 621 jasmine.Env.prototype.setInterval = jasmine.setInterval; 622 jasmine.Env.prototype.clearInterval = jasmine.clearInterval; 623 624 /** 625 * @returns an object containing jasmine version build info, if set. 626 */ 627 jasmine.Env.prototype.version = function () { 628 if (jasmine.version_) { 629 return jasmine.version_; 630 } else { 631 throw new Error('Version not set'); 632 } 633 }; 634 635 /** 636 * @returns a sequential integer starting at 0 637 */ 638 jasmine.Env.prototype.nextSpecId = function () { 639 return this.nextSpecId_++; 640 }; 641 642 /** 643 * @returns a sequential integer starting at 0 644 */ 645 jasmine.Env.prototype.nextSuiteId = function () { 646 return this.nextSuiteId_++; 647 }; 648 649 /** 650 * Register a reporter to receive status updates from Jasmine. 651 * @param {jasmine.Reporter} reporter An object which will receive status updates. 652 */ 653 jasmine.Env.prototype.addReporter = function(reporter) { 654 this.reporter.addReporter(reporter); 655 }; 656 657 jasmine.Env.prototype.execute = function() { 658 this.currentRunner_.execute(); 659 }; 660 661 jasmine.Env.prototype.describe = function(description, specDefinitions) { 662 var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); 663 664 var parentSuite = this.currentSuite; 665 if (parentSuite) { 666 parentSuite.add(suite); 667 } else { 668 this.currentRunner_.add(suite); 669 } 670 671 this.currentSuite = suite; 672 673 specDefinitions.call(suite); 674 675 this.currentSuite = parentSuite; 676 677 return suite; 678 }; 679 680 jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { 681 if (this.currentSuite) { 682 this.currentSuite.beforeEach(beforeEachFunction); 683 } else { 684 this.currentRunner_.beforeEach(beforeEachFunction); 685 } 686 }; 687 688 jasmine.Env.prototype.currentRunner = function () { 689 return this.currentRunner_; 690 }; 691 692 jasmine.Env.prototype.afterEach = function(afterEachFunction) { 693 if (this.currentSuite) { 694 this.currentSuite.afterEach(afterEachFunction); 695 } else { 696 this.currentRunner_.afterEach(afterEachFunction); 697 } 698 699 }; 700 701 jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { 702 return { 703 execute: function() { 704 } 705 }; 706 }; 707 708 jasmine.Env.prototype.it = function(description, func) { 709 var spec = new jasmine.Spec(this, this.currentSuite, description); 710 this.currentSuite.add(spec); 711 this.currentSpec = spec; 712 713 if (func) { 714 spec.runs(func); 715 } 716 717 return spec; 718 }; 719 720 jasmine.Env.prototype.xit = function(desc, func) { 721 return { 722 id: this.nextSpecId(), 723 runs: function() { 724 } 725 }; 726 }; 727 728 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { 729 if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { 730 return true; 731 } 732 733 a.__Jasmine_been_here_before__ = b; 734 b.__Jasmine_been_here_before__ = a; 735 736 var hasKey = function(obj, keyName) { 737 return obj != null && obj[keyName] !== undefined; 738 }; 739 740 for (var property in b) { 741 if (!hasKey(a, property) && hasKey(b, property)) { 742 mismatchKeys.push("expected has key '" + property + "', but missing from <b>actual</b>."); 743 } 744 } 745 for (property in a) { 746 if (!hasKey(b, property) && hasKey(a, property)) { 747 mismatchKeys.push("<b>expected</b> missing key '" + property + "', but present in actual."); 748 } 749 } 750 for (property in b) { 751 if (property == '__Jasmine_been_here_before__') continue; 752 if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { 753 mismatchValues.push("'" + property + "' was<br /><br />'" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "'<br /><br />in expected, but was<br /><br />'" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "'<br /><br />in actual.<br />"); 754 } 755 } 756 757 if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { 758 mismatchValues.push("arrays were not the same length"); 759 } 760 761 delete a.__Jasmine_been_here_before__; 762 delete b.__Jasmine_been_here_before__; 763 return (mismatchKeys.length == 0 && mismatchValues.length == 0); 764 }; 765 766 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { 767 mismatchKeys = mismatchKeys || []; 768 mismatchValues = mismatchValues || []; 769 770 if (a === b) return true; 771 772 if (a === undefined || a === null || b === undefined || b === null) { 773 return (a == undefined && b == undefined); 774 } 775 776 if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { 777 return a === b; 778 } 779 780 if (a instanceof Date && b instanceof Date) { 781 return a.getTime() == b.getTime(); 782 } 783 784 if (a instanceof jasmine.Matchers.Any) { 785 return a.matches(b); 786 } 787 788 if (b instanceof jasmine.Matchers.Any) { 789 return b.matches(a); 790 } 791 792 if (typeof a === "object" && typeof b === "object") { 793 return this.compareObjects_(a, b, mismatchKeys, mismatchValues); 794 } 795 796 for (var i = 0; i < this.equalityTesters_.length; i++) { 797 var equalityTester = this.equalityTesters_[i]; 798 var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); 799 if (result !== undefined) return result; 800 } 801 802 //Straight check 803 return (a === b); 804 }; 805 806 jasmine.Env.prototype.contains_ = function(haystack, needle) { 807 if (jasmine.isArray_(haystack)) { 808 for (var i = 0; i < haystack.length; i++) { 809 if (this.equals_(haystack[i], needle)) return true; 810 } 811 return false; 812 } 813 return haystack.indexOf(needle) >= 0; 814 }; 815 816 jasmine.Env.prototype.addEqualityTester = function(equalityTester) { 817 this.equalityTesters_.push(equalityTester); 818 }; 819 /** No-op base class for Jasmine reporters. 820 * 821 * @constructor 822 */ 823 jasmine.Reporter = function() { 824 }; 825 826 //noinspection JSUnusedLocalSymbols 827 jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { 828 }; 829 830 //noinspection JSUnusedLocalSymbols 831 jasmine.Reporter.prototype.reportRunnerResults = function(runner) { 832 }; 833 834 //noinspection JSUnusedLocalSymbols 835 jasmine.Reporter.prototype.reportSuiteResults = function(suite) { 836 }; 837 838 //noinspection JSUnusedLocalSymbols 839 jasmine.Reporter.prototype.reportSpecResults = function(spec) { 840 }; 841 842 //noinspection JSUnusedLocalSymbols 843 jasmine.Reporter.prototype.log = function(str) { 844 }; 845 846 /** 847 * Blocks are functions with executable code that make up a spec. 848 * 849 * @constructor 850 * @param {jasmine.Env} env 851 * @param {Function} func 852 * @param {jasmine.Spec} spec 853 */ 854 jasmine.Block = function(env, func, spec) { 855 this.env = env; 856 this.func = func; 857 this.spec = spec; 858 }; 859 860 jasmine.Block.prototype.execute = function(onComplete) { 861 try { 862 this.func.apply(this.spec); 863 } catch (e) { 864 this.spec.fail(e); 865 } 866 onComplete(); 867 }; 868 /** JavaScript API reporter. 869 * 870 * @constructor 871 */ 872 jasmine.JsApiReporter = function() { 873 this.started = false; 874 this.finished = false; 875 this.suites_ = []; 876 this.results_ = {}; 877 }; 878 879 jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { 880 this.started = true; 881 var suites = runner.suites(); 882 for (var i = 0; i < suites.length; i++) { 883 var suite = suites[i]; 884 this.suites_.push(this.summarize_(suite)); 885 } 886 }; 887 888 jasmine.JsApiReporter.prototype.suites = function() { 889 return this.suites_; 890 }; 891 892 jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { 893 var isSuite = suiteOrSpec instanceof jasmine.Suite 894 var summary = { 895 id: suiteOrSpec.id, 896 name: suiteOrSpec.description, 897 type: isSuite ? 'suite' : 'spec', 898 children: [] 899 }; 900 if (isSuite) { 901 var specs = suiteOrSpec.specs(); 902 for (var i = 0; i < specs.length; i++) { 903 summary.children.push(this.summarize_(specs[i])); 904 } 905 } 906 return summary; 907 }; 908 909 jasmine.JsApiReporter.prototype.results = function() { 910 return this.results_; 911 }; 912 913 jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { 914 return this.results_[specId]; 915 }; 916 917 //noinspection JSUnusedLocalSymbols 918 jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { 919 this.finished = true; 920 }; 921 922 //noinspection JSUnusedLocalSymbols 923 jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { 924 }; 925 926 //noinspection JSUnusedLocalSymbols 927 jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { 928 this.results_[spec.id] = { 929 messages: spec.results().getItems(), 930 result: spec.results().failedCount > 0 ? "failed" : "passed" 931 }; 932 }; 933 934 //noinspection JSUnusedLocalSymbols 935 jasmine.JsApiReporter.prototype.log = function(str) { 936 }; 937 938 jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ 939 var results = {}; 940 for (var i = 0; i < specIds.length; i++) { 941 var specId = specIds[i]; 942 results[specId] = this.summarizeResult_(this.results_[specId]); 943 } 944 return results; 945 }; 946 947 jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ 948 var summaryMessages = []; 949 for (var messageIndex in result.messages) { 950 var resultMessage = result.messages[messageIndex]; 951 summaryMessages.push({ 952 text: resultMessage.text, 953 passed: resultMessage.passed ? resultMessage.passed() : true, 954 type: resultMessage.type, 955 message: resultMessage.message, 956 trace: { 957 stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : undefined 958 } 959 }); 960 }; 961 962 var summaryResult = { 963 result : result.result, 964 messages : summaryMessages 965 }; 966 967 return summaryResult; 968 }; 969 970 jasmine.Matchers = function(env, actual, results) { 971 this.env = env; 972 this.actual = actual; 973 this.passing_message = 'Passed.'; 974 this.results_ = results || new jasmine.NestedResults(); 975 }; 976 977 jasmine.Matchers.pp = function(str) { 978 return jasmine.util.htmlEscape(jasmine.pp(str)); 979 }; 980 981 /** @deprecated */ 982 jasmine.Matchers.prototype.getResults = function() { 983 return this.results_; 984 }; 985 986 jasmine.Matchers.prototype.results = function() { 987 return this.results_; 988 }; 989 990 jasmine.Matchers.prototype.report = function(result, failing_message, details) { 991 this.results_.addResult(new jasmine.ExpectationResult(result, result ? this.passing_message : failing_message, details)); 992 return result; 993 }; 994 995 /** 996 * Matcher that compares the actual to the expected using ===. 997 * 998 * @param expected 999 */ 1000 jasmine.Matchers.prototype.toBe = function(expected) { 1001 return this.report(this.actual === expected, 'Expected<br /><br />' + jasmine.Matchers.pp(expected) 1002 + '<br /><br />to be the same object as<br /><br />' + jasmine.Matchers.pp(this.actual) 1003 + '<br />'); 1004 }; 1005 1006 /** 1007 * Matcher that compares the actual to the expected using !== 1008 * @param expected 1009 */ 1010 jasmine.Matchers.prototype.toNotBe = function(expected) { 1011 return this.report(this.actual !== expected, 'Expected<br /><br />' + jasmine.Matchers.pp(expected) 1012 + '<br /><br />to be a different object from actual, but they were the same.'); 1013 }; 1014 1015 /** 1016 * Matcher that compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 1017 * 1018 * @param expected 1019 */ 1020 jasmine.Matchers.prototype.toEqual = function(expected) { 1021 var mismatchKeys = []; 1022 var mismatchValues = []; 1023 1024 var formatMismatches = function(name, array) { 1025 if (array.length == 0) return ''; 1026 var errorOutput = '<br /><br />Different ' + name + ':<br />'; 1027 for (var i = 0; i < array.length; i++) { 1028 errorOutput += array[i] + '<br />'; 1029 } 1030 return errorOutput; 1031 }; 1032 1033 return this.report(this.env.equals_(this.actual, expected, mismatchKeys, mismatchValues), 1034 'Expected<br /><br />' + jasmine.Matchers.pp(expected) 1035 + '<br /><br />but got<br /><br />' + jasmine.Matchers.pp(this.actual) 1036 + '<br />' 1037 + formatMismatches('Keys', mismatchKeys) 1038 + formatMismatches('Values', mismatchValues), { 1039 matcherName: 'toEqual', expected: expected, actual: this.actual 1040 }); 1041 }; 1042 /** @deprecated */ 1043 jasmine.Matchers.prototype.should_equal = jasmine.Matchers.prototype.toEqual; 1044 1045 /** 1046 * Matcher that compares the actual to the expected using the ! of jasmine.Matchers.toEqual 1047 * @param expected 1048 */ 1049 jasmine.Matchers.prototype.toNotEqual = function(expected) { 1050 return this.report(!this.env.equals_(this.actual, expected), 1051 'Expected ' + jasmine.Matchers.pp(expected) + ' to not equal ' + jasmine.Matchers.pp(this.actual) + ', but it does.'); 1052 }; 1053 /** @deprecated */ 1054 jasmine.Matchers.prototype.should_not_equal = jasmine.Matchers.prototype.toNotEqual; 1055 1056 /** 1057 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 1058 * a pattern or a String. 1059 * 1060 * @param reg_exp 1061 */ 1062 jasmine.Matchers.prototype.toMatch = function(reg_exp) { 1063 return this.report((new RegExp(reg_exp).test(this.actual)), 1064 'Expected ' + jasmine.Matchers.pp(this.actual) + ' to match ' + reg_exp + '.'); 1065 }; 1066 /** @deprecated */ 1067 jasmine.Matchers.prototype.should_match = jasmine.Matchers.prototype.toMatch; 1068 1069 /** 1070 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 1071 * @param reg_exp 1072 */ 1073 jasmine.Matchers.prototype.toNotMatch = function(reg_exp) { 1074 return this.report((!new RegExp(reg_exp).test(this.actual)), 1075 'Expected ' + jasmine.Matchers.pp(this.actual) + ' to not match ' + reg_exp + '.'); 1076 }; 1077 /** @deprecated */ 1078 jasmine.Matchers.prototype.should_not_match = jasmine.Matchers.prototype.toNotMatch; 1079 1080 /** 1081 * Matcher that compares the acutal to undefined. 1082 */ 1083 jasmine.Matchers.prototype.toBeDefined = function() { 1084 return this.report((this.actual !== undefined), 1085 'Expected a value to be defined but it was undefined.'); 1086 }; 1087 /** @deprecated */ 1088 jasmine.Matchers.prototype.should_be_defined = jasmine.Matchers.prototype.toBeDefined; 1089 1090 /** 1091 * Matcher that compares the actual to null. 1092 * 1093 */ 1094 jasmine.Matchers.prototype.toBeNull = function() { 1095 return this.report((this.actual === null), 1096 'Expected a value to be null but it was ' + jasmine.Matchers.pp(this.actual) + '.'); 1097 }; 1098 /** @deprecated */ 1099 jasmine.Matchers.prototype.should_be_null = jasmine.Matchers.prototype.toBeNull; 1100 1101 /** 1102 * Matcher that boolean not-nots the actual. 1103 */ 1104 jasmine.Matchers.prototype.toBeTruthy = function() { 1105 return this.report(!!this.actual, 1106 'Expected a value to be truthy but it was ' + jasmine.Matchers.pp(this.actual) + '.'); 1107 }; 1108 /** @deprecated */ 1109 jasmine.Matchers.prototype.should_be_truthy = jasmine.Matchers.prototype.toBeTruthy; 1110 1111 /** 1112 * Matcher that boolean nots the actual. 1113 */ 1114 jasmine.Matchers.prototype.toBeFalsy = function() { 1115 return this.report(!this.actual, 1116 'Expected a value to be falsy but it was ' + jasmine.Matchers.pp(this.actual) + '.'); 1117 }; 1118 /** @deprecated */ 1119 jasmine.Matchers.prototype.should_be_falsy = jasmine.Matchers.prototype.toBeFalsy; 1120 1121 /** 1122 * Matcher that checks to see if the acutal, a Jasmine spy, was called. 1123 */ 1124 jasmine.Matchers.prototype.wasCalled = function() { 1125 if (!this.actual || !this.actual.isSpy) { 1126 return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); 1127 } 1128 if (arguments.length > 0) { 1129 return this.report(false, 'wasCalled matcher does not take arguments'); 1130 } 1131 return this.report((this.actual.wasCalled), 1132 'Expected spy "' + this.actual.identity + '" to have been called, but it was not.'); 1133 }; 1134 /** @deprecated */ 1135 jasmine.Matchers.prototype.was_called = jasmine.Matchers.prototype.wasCalled; 1136 1137 /** 1138 * Matcher that checks to see if the acutal, a Jasmine spy, was not called. 1139 */ 1140 jasmine.Matchers.prototype.wasNotCalled = function() { 1141 if (!this.actual || !this.actual.isSpy) { 1142 return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); 1143 } 1144 return this.report((!this.actual.wasCalled), 1145 'Expected spy "' + this.actual.identity + '" to not have been called, but it was.'); 1146 }; 1147 /** @deprecated */ 1148 jasmine.Matchers.prototype.was_not_called = jasmine.Matchers.prototype.wasNotCalled; 1149 1150 /** 1151 * Matcher that checks to see if the acutal, a Jasmine spy, was called with a set of parameters. 1152 * 1153 * @example 1154 * 1155 */ 1156 jasmine.Matchers.prototype.wasCalledWith = function() { 1157 if (!this.actual || !this.actual.isSpy) { 1158 return this.report(false, 'Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.', { 1159 matcherName: 'wasCalledWith' 1160 }); 1161 } 1162 1163 var args = jasmine.util.argsToArray(arguments); 1164 1165 return this.report(this.env.contains_(this.actual.argsForCall, args), 1166 'Expected ' + jasmine.Matchers.pp(this.actual.argsForCall) + ' to contain ' + jasmine.Matchers.pp(args) + ', but it does not.', { 1167 matcherName: 'wasCalledWith', expected: args, actual: this.actual.argsForCall 1168 }); 1169 }; 1170 1171 /** 1172 * Matcher that checks that the expected item is an element in the actual Array. 1173 * 1174 * @param {Object} item 1175 */ 1176 jasmine.Matchers.prototype.toContain = function(item) { 1177 return this.report(this.env.contains_(this.actual, item), 1178 'Expected ' + jasmine.Matchers.pp(this.actual) + ' to contain ' + jasmine.Matchers.pp(item) + ', but it does not.', { 1179 matcherName: 'toContain', expected: item, actual: this.actual 1180 }); 1181 }; 1182 1183 /** 1184 * Matcher that checks that the expected item is NOT an element in the actual Array. 1185 * 1186 * @param {Object} item 1187 */ 1188 jasmine.Matchers.prototype.toNotContain = function(item) { 1189 return this.report(!this.env.contains_(this.actual, item), 1190 'Expected ' + jasmine.Matchers.pp(this.actual) + ' not to contain ' + jasmine.Matchers.pp(item) + ', but it does.'); 1191 }; 1192 1193 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 1194 return this.report(this.actual < expected, 1195 'Expected ' + jasmine.Matchers.pp(this.actual) + ' to be less than ' + jasmine.Matchers.pp(expected) + ', but it was not.'); 1196 }; 1197 1198 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 1199 return this.report(this.actual > expected, 1200 'Expected ' + jasmine.Matchers.pp(this.actual) + ' to be greater than ' + jasmine.Matchers.pp(expected) + ', but it was not.'); 1201 }; 1202 1203 /** 1204 * Matcher that checks that the expected exception was thrown by the actual. 1205 * 1206 * @param {String} expectedException 1207 */ 1208 jasmine.Matchers.prototype.toThrow = function(expectedException) { 1209 var exception = null; 1210 try { 1211 this.actual(); 1212 } catch (e) { 1213 exception = e; 1214 } 1215 if (expectedException !== undefined) { 1216 if (exception == null) { 1217 return this.report(false, "Expected function to throw " + jasmine.Matchers.pp(expectedException) + ", but it did not."); 1218 } 1219 return this.report( 1220 this.env.equals_( 1221 exception.message || exception, 1222 expectedException.message || expectedException), 1223 "Expected function to throw " + jasmine.Matchers.pp(expectedException) + ", but it threw " + jasmine.Matchers.pp(exception) + "."); 1224 } else { 1225 return this.report(exception != null, "Expected function to throw an exception, but it did not."); 1226 } 1227 }; 1228 1229 jasmine.Matchers.Any = function(expectedClass) { 1230 this.expectedClass = expectedClass; 1231 }; 1232 1233 jasmine.Matchers.Any.prototype.matches = function(other) { 1234 if (this.expectedClass == String) { 1235 return typeof other == 'string' || other instanceof String; 1236 } 1237 1238 if (this.expectedClass == Number) { 1239 return typeof other == 'number' || other instanceof Number; 1240 } 1241 1242 if (this.expectedClass == Function) { 1243 return typeof other == 'function' || other instanceof Function; 1244 } 1245 1246 if (this.expectedClass == Object) { 1247 return typeof other == 'object'; 1248 } 1249 1250 return other instanceof this.expectedClass; 1251 }; 1252 1253 jasmine.Matchers.Any.prototype.toString = function() { 1254 return '<jasmine.any(' + this.expectedClass + ')>'; 1255 }; 1256 1257 /** 1258 * @constructor 1259 */ 1260 jasmine.MultiReporter = function() { 1261 this.subReporters_ = []; 1262 }; 1263 jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); 1264 1265 jasmine.MultiReporter.prototype.addReporter = function(reporter) { 1266 this.subReporters_.push(reporter); 1267 }; 1268 1269 (function() { 1270 var functionNames = ["reportRunnerStarting", "reportRunnerResults", "reportSuiteResults", "reportSpecResults", "log"]; 1271 for (var i = 0; i < functionNames.length; i++) { 1272 var functionName = functionNames[i]; 1273 jasmine.MultiReporter.prototype[functionName] = (function(functionName) { 1274 return function() { 1275 for (var j = 0; j < this.subReporters_.length; j++) { 1276 var subReporter = this.subReporters_[j]; 1277 if (subReporter[functionName]) { 1278 subReporter[functionName].apply(subReporter, arguments); 1279 } 1280 } 1281 }; 1282 })(functionName); 1283 } 1284 })(); 1285 /** 1286 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults 1287 * 1288 * @constructor 1289 */ 1290 jasmine.NestedResults = function() { 1291 /** 1292 * The total count of results 1293 */ 1294 this.totalCount = 0; 1295 /** 1296 * Number of passed results 1297 */ 1298 this.passedCount = 0; 1299 /** 1300 * Number of failed results 1301 */ 1302 this.failedCount = 0; 1303 /** 1304 * Was this suite/spec skipped? 1305 */ 1306 this.skipped = false; 1307 /** 1308 * @ignore 1309 */ 1310 this.items_ = []; 1311 }; 1312 1313 /** 1314 * Roll up the result counts. 1315 * 1316 * @param result 1317 */ 1318 jasmine.NestedResults.prototype.rollupCounts = function(result) { 1319 this.totalCount += result.totalCount; 1320 this.passedCount += result.passedCount; 1321 this.failedCount += result.failedCount; 1322 }; 1323 1324 /** 1325 * Tracks a result's message. 1326 * @param message 1327 */ 1328 jasmine.NestedResults.prototype.log = function(message) { 1329 this.items_.push(new jasmine.MessageResult(message)); 1330 }; 1331 1332 /** 1333 * Getter for the results: message & results. 1334 */ 1335 jasmine.NestedResults.prototype.getItems = function() { 1336 return this.items_; 1337 }; 1338 1339 /** 1340 * Adds a result, tracking counts (total, passed, & failed) 1341 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result 1342 */ 1343 jasmine.NestedResults.prototype.addResult = function(result) { 1344 if (result.type != 'MessageResult') { 1345 if (result.items_) { 1346 this.rollupCounts(result); 1347 } else { 1348 this.totalCount++; 1349 if (result.passed()) { 1350 this.passedCount++; 1351 } else { 1352 this.failedCount++; 1353 } 1354 } 1355 } 1356 this.items_.push(result); 1357 }; 1358 1359 /** 1360 * @returns {Boolean} True if <b>everything</b> below passed 1361 */ 1362 jasmine.NestedResults.prototype.passed = function() { 1363 return this.passedCount === this.totalCount; 1364 }; 1365 /** 1366 * Base class for pretty printing for expectation results. 1367 */ 1368 jasmine.PrettyPrinter = function() { 1369 this.ppNestLevel_ = 0; 1370 }; 1371 1372 /** 1373 * Formats a value in a nice, human-readable string. 1374 * 1375 * @param value 1376 * @returns {String} 1377 */ 1378 jasmine.PrettyPrinter.prototype.format = function(value) { 1379 if (this.ppNestLevel_ > 40) { 1380 // return '(jasmine.pp nested too deeply!)'; 1381 throw new Error('jasmine.PrettyPrinter: format() nested too deeply!'); 1382 } 1383 1384 this.ppNestLevel_++; 1385 try { 1386 if (value === undefined) { 1387 this.emitScalar('undefined'); 1388 } else if (value === null) { 1389 this.emitScalar('null'); 1390 } else if (value.navigator && value.frames && value.setTimeout) { 1391 this.emitScalar('<window>'); 1392 } else if (value instanceof jasmine.Matchers.Any) { 1393 this.emitScalar(value.toString()); 1394 } else if (typeof value === 'string') { 1395 this.emitString(value); 1396 } else if (typeof value === 'function') { 1397 this.emitScalar('Function'); 1398 } else if (typeof value.nodeType === 'number') { 1399 this.emitScalar('HTMLNode'); 1400 } else if (value instanceof Date) { 1401 this.emitScalar('Date(' + value + ')'); 1402 } else if (value.__Jasmine_been_here_before__) { 1403 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>'); 1404 } else if (jasmine.isArray_(value) || typeof value == 'object') { 1405 value.__Jasmine_been_here_before__ = true; 1406 if (jasmine.isArray_(value)) { 1407 this.emitArray(value); 1408 } else { 1409 this.emitObject(value); 1410 } 1411 delete value.__Jasmine_been_here_before__; 1412 } else { 1413 this.emitScalar(value.toString()); 1414 } 1415 } finally { 1416 this.ppNestLevel_--; 1417 } 1418 }; 1419 1420 jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1421 for (var property in obj) { 1422 if (property == '__Jasmine_been_here_before__') continue; 1423 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) != null) : false); 1424 } 1425 }; 1426 1427 jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; 1428 jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; 1429 jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; 1430 jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; 1431 1432 jasmine.StringPrettyPrinter = function() { 1433 jasmine.PrettyPrinter.call(this); 1434 1435 this.string = ''; 1436 }; 1437 jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); 1438 1439 jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { 1440 this.append(value); 1441 }; 1442 1443 jasmine.StringPrettyPrinter.prototype.emitString = function(value) { 1444 this.append("'" + value + "'"); 1445 }; 1446 1447 jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { 1448 this.append('[ '); 1449 for (var i = 0; i < array.length; i++) { 1450 if (i > 0) { 1451 this.append(', '); 1452 } 1453 this.format(array[i]); 1454 } 1455 this.append(' ]'); 1456 }; 1457 1458 jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { 1459 var self = this; 1460 this.append('{ '); 1461 var first = true; 1462 1463 this.iterateObject(obj, function(property, isGetter) { 1464 if (first) { 1465 first = false; 1466 } else { 1467 self.append(', '); 1468 } 1469 1470 self.append(property); 1471 self.append(' : '); 1472 if (isGetter) { 1473 self.append('<getter>'); 1474 } else { 1475 self.format(obj[property]); 1476 } 1477 }); 1478 1479 this.append(' }'); 1480 }; 1481 1482 jasmine.StringPrettyPrinter.prototype.append = function(value) { 1483 this.string += value; 1484 }; 1485 jasmine.Queue = function(env) { 1486 this.env = env; 1487 this.blocks = []; 1488 this.running = false; 1489 this.index = 0; 1490 this.offset = 0; 1491 }; 1492 1493 jasmine.Queue.prototype.addBefore = function(block) { 1494 this.blocks.unshift(block); 1495 }; 1496 1497 jasmine.Queue.prototype.add = function(block) { 1498 this.blocks.push(block); 1499 }; 1500 1501 jasmine.Queue.prototype.insertNext = function(block) { 1502 this.blocks.splice((this.index + this.offset + 1), 0, block); 1503 this.offset++; 1504 }; 1505 1506 jasmine.Queue.prototype.start = function(onComplete) { 1507 this.running = true; 1508 this.onComplete = onComplete; 1509 this.next_(); 1510 }; 1511 1512 jasmine.Queue.prototype.isRunning = function() { 1513 return this.running; 1514 }; 1515 1516 jasmine.Queue.LOOP_DONT_RECURSE = true; 1517 1518 jasmine.Queue.prototype.next_ = function() { 1519 var self = this; 1520 var goAgain = true; 1521 1522 while (goAgain) { 1523 goAgain = false; 1524 1525 if (self.index < self.blocks.length) { 1526 var calledSynchronously = true; 1527 var completedSynchronously = false; 1528 1529 var onComplete = function () { 1530 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { 1531 completedSynchronously = true; 1532 return; 1533 } 1534 1535 self.offset = 0; 1536 self.index++; 1537 1538 var now = new Date().getTime(); 1539 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { 1540 self.env.lastUpdate = now; 1541 self.env.setTimeout(function() { 1542 self.next_(); 1543 }, 0); 1544 } else { 1545 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { 1546 goAgain = true; 1547 } else { 1548 self.next_(); 1549 } 1550 } 1551 }; 1552 self.blocks[self.index].execute(onComplete); 1553 1554 calledSynchronously = false; 1555 if (completedSynchronously) { 1556 onComplete(); 1557 } 1558 1559 } else { 1560 self.running = false; 1561 if (self.onComplete) { 1562 self.onComplete(); 1563 } 1564 } 1565 } 1566 }; 1567 1568 jasmine.Queue.prototype.results = function() { 1569 var results = new jasmine.NestedResults(); 1570 for (var i = 0; i < this.blocks.length; i++) { 1571 if (this.blocks[i].results) { 1572 results.addResult(this.blocks[i].results()); 1573 } 1574 } 1575 return results; 1576 }; 1577 1578 1579 /* JasmineReporters.reporter 1580 * Base object that will get called whenever a Spec, Suite, or Runner is done. It is up to 1581 * descendants of this object to do something with the results (see json_reporter.js) 1582 */ 1583 jasmine.Reporters = {}; 1584 1585 jasmine.Reporters.reporter = function(callbacks) { 1586 var that = { 1587 callbacks: callbacks || {}, 1588 1589 doCallback: function(callback, results) { 1590 if (callback) { 1591 callback(results); 1592 } 1593 }, 1594 1595 reportRunnerResults: function(runner) { 1596 that.doCallback(that.callbacks.runnerCallback, runner); 1597 }, 1598 reportSuiteResults: function(suite) { 1599 that.doCallback(that.callbacks.suiteCallback, suite); 1600 }, 1601 reportSpecResults: function(spec) { 1602 that.doCallback(that.callbacks.specCallback, spec); 1603 }, 1604 log: function (str) { 1605 if (console && console.log) console.log(str); 1606 } 1607 }; 1608 1609 return that; 1610 }; 1611 1612 /** 1613 * Runner 1614 * 1615 * @constructor 1616 * @param {jasmine.Env} env 1617 */ 1618 jasmine.Runner = function(env) { 1619 var self = this; 1620 self.env = env; 1621 self.queue = new jasmine.Queue(env); 1622 self.before_ = []; 1623 self.after_ = []; 1624 self.suites_ = []; 1625 }; 1626 1627 jasmine.Runner.prototype.execute = function() { 1628 var self = this; 1629 if (self.env.reporter.reportRunnerStarting) { 1630 self.env.reporter.reportRunnerStarting(this); 1631 } 1632 self.queue.start(function () { 1633 self.finishCallback(); 1634 }); 1635 }; 1636 1637 jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { 1638 beforeEachFunction.typeName = 'beforeEach'; 1639 this.before_.push(beforeEachFunction); 1640 }; 1641 1642 jasmine.Runner.prototype.afterEach = function(afterEachFunction) { 1643 afterEachFunction.typeName = 'afterEach'; 1644 this.after_.push(afterEachFunction); 1645 }; 1646 1647 1648 jasmine.Runner.prototype.finishCallback = function() { 1649 this.env.reporter.reportRunnerResults(this); 1650 }; 1651 1652 jasmine.Runner.prototype.addSuite = function(suite) { 1653 this.suites_.push(suite); 1654 }; 1655 1656 jasmine.Runner.prototype.add = function(block) { 1657 if (block instanceof jasmine.Suite) { 1658 this.addSuite(block); 1659 } 1660 this.queue.add(block); 1661 }; 1662 1663 /** @deprecated */ 1664 jasmine.Runner.prototype.getAllSuites = function() { 1665 return this.suites_; 1666 }; 1667 1668 jasmine.Runner.prototype.specs = function () { 1669 var suites = this.suites(); 1670 var specs = []; 1671 for (var i = 0; i < suites.length; i++) { 1672 specs = specs.concat(suites[i].specs()); 1673 } 1674 return specs; 1675 }; 1676 1677 1678 jasmine.Runner.prototype.suites = function() { 1679 return this.suites_; 1680 }; 1681 1682 jasmine.Runner.prototype.results = function() { 1683 return this.queue.results(); 1684 }; 1685 1686 /** @deprecated */ 1687 jasmine.Runner.prototype.getResults = function() { 1688 return this.queue.results(); 1689 }; 1690 /** 1691 * Internal representation of a Jasmine specification, or test. 1692 * 1693 * @constructor 1694 * @param {jasmine.Env} env 1695 * @param {jasmine.Suite} suite 1696 * @param {String} description 1697 */ 1698 jasmine.Spec = function(env, suite, description) { 1699 if (!env) { 1700 throw new Error('jasmine.Env() required'); 1701 } 1702 ; 1703 if (!suite) { 1704 throw new Error('jasmine.Suite() required'); 1705 } 1706 ; 1707 var spec = this; 1708 spec.id = env.nextSpecId ? env.nextSpecId() : null; 1709 spec.env = env; 1710 spec.suite = suite; 1711 spec.description = description; 1712 spec.queue = new jasmine.Queue(env); 1713 1714 spec.afterCallbacks = []; 1715 spec.spies_ = []; 1716 1717 spec.results_ = new jasmine.NestedResults(); 1718 spec.results_.description = description; 1719 spec.matchersClass = null; 1720 }; 1721 1722 jasmine.Spec.prototype.getFullName = function() { 1723 return this.suite.getFullName() + ' ' + this.description + '.'; 1724 }; 1725 1726 1727 jasmine.Spec.prototype.results = function() { 1728 return this.results_; 1729 }; 1730 1731 jasmine.Spec.prototype.log = function(message) { 1732 return this.results_.log(message); 1733 }; 1734 1735 /** @deprecated */ 1736 jasmine.Spec.prototype.getResults = function() { 1737 return this.results_; 1738 }; 1739 1740 jasmine.Spec.prototype.runs = function (func) { 1741 var block = new jasmine.Block(this.env, func, this); 1742 this.addToQueue(block); 1743 return this; 1744 }; 1745 1746 jasmine.Spec.prototype.addToQueue = function (block) { 1747 if (this.queue.isRunning()) { 1748 this.queue.insertNext(block); 1749 } else { 1750 this.queue.add(block); 1751 } 1752 }; 1753 1754 /** 1755 * @private 1756 * @deprecated 1757 */ 1758 jasmine.Spec.prototype.expects_that = function(actual) { 1759 return this.expect(actual); 1760 }; 1761 1762 jasmine.Spec.prototype.expect = function(actual) { 1763 return new (this.getMatchersClass_())(this.env, actual, this.results_); 1764 }; 1765 1766 jasmine.Spec.prototype.waits = function(timeout) { 1767 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 1768 this.addToQueue(waitsFunc); 1769 return this; 1770 }; 1771 1772 jasmine.Spec.prototype.waitsFor = function(timeout, latchFunction, timeoutMessage) { 1773 var waitsForFunc = new jasmine.WaitsForBlock(this.env, timeout, latchFunction, timeoutMessage, this); 1774 this.addToQueue(waitsForFunc); 1775 return this; 1776 }; 1777 1778 jasmine.Spec.prototype.fail = function (e) { 1779 this.results_.addResult(new jasmine.ExpectationResult(false, e ? jasmine.util.formatException(e) : null, null)); 1780 }; 1781 1782 jasmine.Spec.prototype.getMatchersClass_ = function() { 1783 return this.matchersClass || jasmine.Matchers; 1784 }; 1785 1786 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 1787 var parent = this.getMatchersClass_(); 1788 var newMatchersClass = function() { 1789 parent.apply(this, arguments); 1790 }; 1791 jasmine.util.inherit(newMatchersClass, parent); 1792 for (var method in matchersPrototype) { 1793 newMatchersClass.prototype[method] = matchersPrototype[method]; 1794 } 1795 this.matchersClass = newMatchersClass; 1796 }; 1797 1798 jasmine.Spec.prototype.finishCallback = function() { 1799 this.env.reporter.reportSpecResults(this); 1800 }; 1801 1802 jasmine.Spec.prototype.finish = function(onComplete) { 1803 this.removeAllSpies(); 1804 this.finishCallback(); 1805 if (onComplete) { 1806 onComplete(); 1807 } 1808 }; 1809 1810 jasmine.Spec.prototype.after = function(doAfter, test) { 1811 1812 if (this.queue.isRunning()) { 1813 this.queue.add(new jasmine.Block(this.env, doAfter, this)); 1814 } else { 1815 this.afterCallbacks.unshift(doAfter); 1816 } 1817 }; 1818 1819 jasmine.Spec.prototype.execute = function(onComplete) { 1820 var spec = this; 1821 if (!spec.env.specFilter(spec)) { 1822 spec.results_.skipped = true; 1823 spec.finish(onComplete); 1824 return; 1825 } 1826 this.env.reporter.log('>> Jasmine Running ' + this.suite.description + ' ' + this.description + '...'); 1827 1828 spec.env.currentSpec = spec; 1829 1830 spec.addBeforesAndAftersToQueue(); 1831 1832 spec.queue.start(function () { 1833 spec.finish(onComplete); 1834 }); 1835 }; 1836 1837 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 1838 var runner = this.env.currentRunner(); 1839 for (var suite = this.suite; suite; suite = suite.parentSuite) { 1840 for (var i = 0; i < suite.before_.length; i++) { 1841 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 1842 } 1843 } 1844 for (var i = 0; i < runner.before_.length; i++) { 1845 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 1846 } 1847 for (i = 0; i < this.afterCallbacks.length; i++) { 1848 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this)); 1849 } 1850 for (suite = this.suite; suite; suite = suite.parentSuite) { 1851 for (var i = 0; i < suite.after_.length; i++) { 1852 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this)); 1853 } 1854 } 1855 for (var i = 0; i < runner.after_.length; i++) { 1856 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this)); 1857 } 1858 }; 1859 1860 jasmine.Spec.prototype.explodes = function() { 1861 throw 'explodes function should not have been called'; 1862 }; 1863 1864 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 1865 if (obj == undefined) { 1866 throw "spyOn could not find an object to spy upon for " + methodName + "()"; 1867 } 1868 1869 if (!ignoreMethodDoesntExist && obj[methodName] === undefined) { 1870 throw methodName + '() method does not exist'; 1871 } 1872 1873 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 1874 throw new Error(methodName + ' has already been spied upon'); 1875 } 1876 1877 var spyObj = jasmine.createSpy(methodName); 1878 1879 this.spies_.push(spyObj); 1880 spyObj.baseObj = obj; 1881 spyObj.methodName = methodName; 1882 spyObj.originalValue = obj[methodName]; 1883 1884 obj[methodName] = spyObj; 1885 1886 return spyObj; 1887 }; 1888 1889 jasmine.Spec.prototype.removeAllSpies = function() { 1890 for (var i = 0; i < this.spies_.length; i++) { 1891 var spy = this.spies_[i]; 1892 spy.baseObj[spy.methodName] = spy.originalValue; 1893 } 1894 this.spies_ = []; 1895 }; 1896 1897 /** 1898 * Internal representation of a Jasmine suite. 1899 * 1900 * @constructor 1901 * @param {jasmine.Env} env 1902 * @param {String} description 1903 * @param {Function} specDefinitions 1904 * @param {jasmine.Suite} parentSuite 1905 */ 1906 jasmine.Suite = function(env, description, specDefinitions, parentSuite) { 1907 var self = this; 1908 self.id = env.nextSuiteId ? env.nextSuiteId() : null; 1909 self.description = description; 1910 self.queue = new jasmine.Queue(env); 1911 self.parentSuite = parentSuite; 1912 self.env = env; 1913 self.before_ = []; 1914 self.after_ = []; 1915 self.specs_ = []; 1916 }; 1917 1918 jasmine.Suite.prototype.getFullName = function() { 1919 var fullName = this.description; 1920 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 1921 fullName = parentSuite.description + ' ' + fullName; 1922 } 1923 return fullName; 1924 }; 1925 1926 jasmine.Suite.prototype.finish = function(onComplete) { 1927 this.env.reporter.reportSuiteResults(this); 1928 this.finished = true; 1929 if (typeof(onComplete) == 'function') { 1930 onComplete(); 1931 } 1932 }; 1933 1934 jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { 1935 beforeEachFunction.typeName = 'beforeEach'; 1936 this.before_.push(beforeEachFunction); 1937 }; 1938 1939 jasmine.Suite.prototype.afterEach = function(afterEachFunction) { 1940 afterEachFunction.typeName = 'afterEach'; 1941 this.after_.push(afterEachFunction); 1942 }; 1943 1944 /** @deprecated */ 1945 jasmine.Suite.prototype.getResults = function() { 1946 return this.queue.results(); 1947 }; 1948 1949 jasmine.Suite.prototype.results = function() { 1950 return this.queue.results(); 1951 }; 1952 1953 jasmine.Suite.prototype.add = function(block) { 1954 if (block instanceof jasmine.Suite) { 1955 this.env.currentRunner().addSuite(block); 1956 } else { 1957 this.specs_.push(block); 1958 } 1959 this.queue.add(block); 1960 }; 1961 1962 /** @deprecated */ 1963 jasmine.Suite.prototype.specCount = function() { 1964 return this.specs_.length; 1965 }; 1966 1967 jasmine.Suite.prototype.specs = function() { 1968 return this.specs_; 1969 }; 1970 1971 jasmine.Suite.prototype.execute = function(onComplete) { 1972 var self = this; 1973 this.queue.start(function () { 1974 self.finish(onComplete); 1975 }); 1976 }; 1977 jasmine.WaitsBlock = function(env, timeout, spec) { 1978 this.timeout = timeout; 1979 jasmine.Block.call(this, env, null, spec); 1980 }; 1981 1982 jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); 1983 1984 jasmine.WaitsBlock.prototype.execute = function (onComplete) { 1985 this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); 1986 this.env.setTimeout(function () { 1987 onComplete(); 1988 }, this.timeout); 1989 }; 1990 jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { 1991 this.timeout = timeout; 1992 this.latchFunction = latchFunction; 1993 this.message = message; 1994 this.totalTimeSpentWaitingForLatch = 0; 1995 jasmine.Block.call(this, env, null, spec); 1996 }; 1997 1998 jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); 1999 2000 jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 100; 2001 2002 jasmine.WaitsForBlock.prototype.execute = function (onComplete) { 2003 var self = this; 2004 self.env.reporter.log('>> Jasmine waiting for ' + (self.message || 'something to happen')); 2005 var latchFunctionResult; 2006 try { 2007 latchFunctionResult = self.latchFunction.apply(self.spec); 2008 } catch (e) { 2009 self.spec.fail(e); 2010 onComplete(); 2011 return; 2012 } 2013 2014 if (latchFunctionResult) { 2015 onComplete(); 2016 } else if (self.totalTimeSpentWaitingForLatch >= self.timeout) { 2017 var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen'); 2018 self.spec.fail({ 2019 name: 'timeout', 2020 message: message 2021 }); 2022 self.spec._next(); 2023 } else { 2024 self.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; 2025 self.env.setTimeout(function () { self.execute(onComplete); }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); 2026 } 2027 }; 2028 // Mock setTimeout, clearTimeout 2029 // Contributed by Pivotal Computer Systems, www.pivotalsf.com 2030 2031 jasmine.FakeTimer = function() { 2032 this.reset(); 2033 2034 var self = this; 2035 self.setTimeout = function(funcToCall, millis) { 2036 self.timeoutsMade++; 2037 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); 2038 return self.timeoutsMade; 2039 }; 2040 2041 self.setInterval = function(funcToCall, millis) { 2042 self.timeoutsMade++; 2043 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); 2044 return self.timeoutsMade; 2045 }; 2046 2047 self.clearTimeout = function(timeoutKey) { 2048 self.scheduledFunctions[timeoutKey] = undefined; 2049 }; 2050 2051 self.clearInterval = function(timeoutKey) { 2052 self.scheduledFunctions[timeoutKey] = undefined; 2053 }; 2054 2055 }; 2056 2057 jasmine.FakeTimer.prototype.reset = function() { 2058 this.timeoutsMade = 0; 2059 this.scheduledFunctions = {}; 2060 this.nowMillis = 0; 2061 }; 2062 2063 jasmine.FakeTimer.prototype.tick = function(millis) { 2064 var oldMillis = this.nowMillis; 2065 var newMillis = oldMillis + millis; 2066 this.runFunctionsWithinRange(oldMillis, newMillis); 2067 this.nowMillis = newMillis; 2068 }; 2069 2070 jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { 2071 var scheduledFunc; 2072 var funcsToRun = []; 2073 for (var timeoutKey in this.scheduledFunctions) { 2074 scheduledFunc = this.scheduledFunctions[timeoutKey]; 2075 if (scheduledFunc != undefined && 2076 scheduledFunc.runAtMillis >= oldMillis && 2077 scheduledFunc.runAtMillis <= nowMillis) { 2078 funcsToRun.push(scheduledFunc); 2079 this.scheduledFunctions[timeoutKey] = undefined; 2080 } 2081 } 2082 2083 if (funcsToRun.length > 0) { 2084 funcsToRun.sort(function(a, b) { 2085 return a.runAtMillis - b.runAtMillis; 2086 }); 2087 for (var i = 0; i < funcsToRun.length; ++i) { 2088 try { 2089 var funcToRun = funcsToRun[i]; 2090 this.nowMillis = funcToRun.runAtMillis; 2091 funcToRun.funcToCall(); 2092 if (funcToRun.recurring) { 2093 this.scheduleFunction(funcToRun.timeoutKey, 2094 funcToRun.funcToCall, 2095 funcToRun.millis, 2096 true); 2097 } 2098 } catch(e) { 2099 } 2100 } 2101 this.runFunctionsWithinRange(oldMillis, nowMillis); 2102 } 2103 }; 2104 2105 jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { 2106 this.scheduledFunctions[timeoutKey] = { 2107 runAtMillis: this.nowMillis + millis, 2108 funcToCall: funcToCall, 2109 recurring: recurring, 2110 timeoutKey: timeoutKey, 2111 millis: millis 2112 }; 2113 }; 2114 2115 2116 jasmine.Clock = { 2117 defaultFakeTimer: new jasmine.FakeTimer(), 2118 2119 reset: function() { 2120 jasmine.Clock.assertInstalled(); 2121 jasmine.Clock.defaultFakeTimer.reset(); 2122 }, 2123 2124 tick: function(millis) { 2125 jasmine.Clock.assertInstalled(); 2126 jasmine.Clock.defaultFakeTimer.tick(millis); 2127 }, 2128 2129 runFunctionsWithinRange: function(oldMillis, nowMillis) { 2130 jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); 2131 }, 2132 2133 scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { 2134 jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); 2135 }, 2136 2137 useMock: function() { 2138 var spec = jasmine.getEnv().currentSpec; 2139 spec.after(jasmine.Clock.uninstallMock); 2140 2141 jasmine.Clock.installMock(); 2142 }, 2143 2144 installMock: function() { 2145 jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; 2146 }, 2147 2148 uninstallMock: function() { 2149 jasmine.Clock.assertInstalled(); 2150 jasmine.Clock.installed = jasmine.Clock.real; 2151 }, 2152 2153 real: { 2154 setTimeout: window.setTimeout, 2155 clearTimeout: window.clearTimeout, 2156 setInterval: window.setInterval, 2157 clearInterval: window.clearInterval 2158 }, 2159 2160 assertInstalled: function() { 2161 if (jasmine.Clock.installed != jasmine.Clock.defaultFakeTimer) { 2162 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); 2163 } 2164 }, 2165 2166 installed: null 2167 }; 2168 jasmine.Clock.installed = jasmine.Clock.real; 2169 2170 //else for IE support 2171 window.setTimeout = function(funcToCall, millis) { 2172 if (jasmine.Clock.installed.setTimeout.apply) { 2173 return jasmine.Clock.installed.setTimeout.apply(this, arguments); 2174 } else { 2175 return jasmine.Clock.installed.setTimeout(funcToCall, millis); 2176 } 2177 }; 2178 2179 window.setInterval = function(funcToCall, millis) { 2180 if (jasmine.Clock.installed.setInterval.apply) { 2181 return jasmine.Clock.installed.setInterval.apply(this, arguments); 2182 } else { 2183 return jasmine.Clock.installed.setInterval(funcToCall, millis); 2184 } 2185 }; 2186 2187 window.clearTimeout = function(timeoutKey) { 2188 if (jasmine.Clock.installed.clearTimeout.apply) { 2189 return jasmine.Clock.installed.clearTimeout.apply(this, arguments); 2190 } else { 2191 return jasmine.Clock.installed.clearTimeout(timeoutKey); 2192 } 2193 }; 2194 2195 window.clearInterval = function(timeoutKey) { 2196 if (jasmine.Clock.installed.clearTimeout.apply) { 2197 return jasmine.Clock.installed.clearInterval.apply(this, arguments); 2198 } else { 2199 return jasmine.Clock.installed.clearInterval(timeoutKey); 2200 } 2201 }; 2202 2203