jasmine.js
1 // Modified line 2 // - var isCommonJS = typeof window == "undefined" && typeof exports == "object"; 3 // + var isCommonJS = typeof exports == "object"; 4 // 5 // Modified method jasmine.WaitsForBlock.prototype.execute 6 7 var isCommonJS = typeof exports == "object"; 8 9 /** 10 * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework. 11 * 12 * @namespace 13 */ 14 var jasmine = {}; 15 if (isCommonJS) exports.jasmine = jasmine; 16 /** 17 * @private 18 */ 19 jasmine.unimplementedMethod_ = function() { 20 throw new Error("unimplemented method"); 21 }; 22 23 /** 24 * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just 25 * a plain old variable and may be redefined by somebody else. 26 * 27 * @private 28 */ 29 jasmine.undefined = jasmine.___undefined___; 30 31 /** 32 * Show diagnostic messages in the console if set to true 33 * 34 */ 35 jasmine.VERBOSE = false; 36 37 /** 38 * 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. 39 * 40 */ 41 jasmine.DEFAULT_UPDATE_INTERVAL = 250; 42 43 /** 44 * Maximum levels of nesting that will be included when an object is pretty-printed 45 */ 46 jasmine.MAX_PRETTY_PRINT_DEPTH = 40; 47 48 /** 49 * Default timeout interval in milliseconds for waitsFor() blocks. 50 */ 51 jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000; 52 53 /** 54 * By default exceptions thrown in the context of a test are caught by jasmine so that it can run the remaining tests in the suite. 55 * Set to false to let the exception bubble up in the browser. 56 * 57 */ 58 jasmine.CATCH_EXCEPTIONS = true; 59 60 jasmine.getGlobal = function() { 61 function getGlobal() { 62 return window; 63 } 64 65 return getGlobal(); 66 }; 67 68 /** 69 * Allows for bound functions to be compared. Internal use only. 70 * 71 * @ignore 72 * @private 73 * @param base {Object} bound 'this' for the function 74 * @param name {Function} function to find 75 */ 76 jasmine.bindOriginal_ = function(base, name) { 77 var original = base[name]; 78 if (original.apply) { 79 return function() { 80 return original.apply(base, arguments); 81 }; 82 } else { 83 // IE support 84 return jasmine.getGlobal()[name]; 85 } 86 }; 87 88 jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout'); 89 jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout'); 90 jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval'); 91 jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval'); 92 93 jasmine.MessageResult = function(values) { 94 this.type = 'log'; 95 this.values = values; 96 this.trace = new Error(); // todo: test better 97 }; 98 99 jasmine.MessageResult.prototype.toString = function() { 100 var text = ""; 101 for (var i = 0; i < this.values.length; i++) { 102 if (i > 0) text += " "; 103 if (jasmine.isString_(this.values[i])) { 104 text += this.values[i]; 105 } else { 106 text += jasmine.pp(this.values[i]); 107 } 108 } 109 return text; 110 }; 111 112 jasmine.ExpectationResult = function(params) { 113 this.type = 'expect'; 114 this.matcherName = params.matcherName; 115 this.passed_ = params.passed; 116 this.expected = params.expected; 117 this.actual = params.actual; 118 this.message = this.passed_ ? 'Passed.' : params.message; 119 120 var trace = (params.trace || new Error(this.message)); 121 this.trace = this.passed_ ? '' : trace; 122 }; 123 124 jasmine.ExpectationResult.prototype.toString = function () { 125 return this.message; 126 }; 127 128 jasmine.ExpectationResult.prototype.passed = function () { 129 return this.passed_; 130 }; 131 132 /** 133 * Getter for the Jasmine environment. Ensures one gets created 134 */ 135 jasmine.getEnv = function() { 136 var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env(); 137 return env; 138 }; 139 140 /** 141 * @ignore 142 * @private 143 * @param value 144 * @returns {Boolean} 145 */ 146 jasmine.isArray_ = function(value) { 147 return jasmine.isA_("Array", value); 148 }; 149 150 /** 151 * @ignore 152 * @private 153 * @param value 154 * @returns {Boolean} 155 */ 156 jasmine.isString_ = function(value) { 157 return jasmine.isA_("String", value); 158 }; 159 160 /** 161 * @ignore 162 * @private 163 * @param value 164 * @returns {Boolean} 165 */ 166 jasmine.isNumber_ = function(value) { 167 return jasmine.isA_("Number", value); 168 }; 169 170 /** 171 * @ignore 172 * @private 173 * @param {String} typeName 174 * @param value 175 * @returns {Boolean} 176 */ 177 jasmine.isA_ = function(typeName, value) { 178 return Object.prototype.toString.apply(value) === '[object ' + typeName + ']'; 179 }; 180 181 /** 182 * Pretty printer for expecations. Takes any object and turns it into a human-readable string. 183 * 184 * @param value {Object} an object to be outputted 185 * @returns {String} 186 */ 187 jasmine.pp = function(value) { 188 var stringPrettyPrinter = new jasmine.StringPrettyPrinter(); 189 stringPrettyPrinter.format(value); 190 return stringPrettyPrinter.string; 191 }; 192 193 /** 194 * Returns true if the object is a DOM Node. 195 * 196 * @param {Object} obj object to check 197 * @returns {Boolean} 198 */ 199 jasmine.isDomNode = function(obj) { 200 return obj.nodeType > 0; 201 }; 202 203 /** 204 * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter. 205 * 206 * @example 207 * // don't care about which function is passed in, as long as it's a function 208 * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function)); 209 * 210 * @param {Class} clazz 211 * @returns matchable object of the type clazz 212 */ 213 jasmine.any = function(clazz) { 214 return new jasmine.Matchers.Any(clazz); 215 }; 216 217 /** 218 * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the 219 * attributes on the object. 220 * 221 * @example 222 * // don't care about any other attributes than foo. 223 * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"}); 224 * 225 * @param sample {Object} sample 226 * @returns matchable object for the sample 227 */ 228 jasmine.objectContaining = function (sample) { 229 return new jasmine.Matchers.ObjectContaining(sample); 230 }; 231 232 /** 233 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks. 234 * 235 * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine 236 * expectation syntax. Spies can be checked if they were called or not and what the calling params were. 237 * 238 * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs). 239 * 240 * Spies are torn down at the end of every spec. 241 * 242 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj. 243 * 244 * @example 245 * // a stub 246 * var myStub = jasmine.createSpy('myStub'); // can be used anywhere 247 * 248 * // spy example 249 * var foo = { 250 * not: function(bool) { return !bool; } 251 * } 252 * 253 * // actual foo.not will not be called, execution stops 254 * spyOn(foo, 'not'); 255 256 // foo.not spied upon, execution will continue to implementation 257 * spyOn(foo, 'not').andCallThrough(); 258 * 259 * // fake example 260 * var foo = { 261 * not: function(bool) { return !bool; } 262 * } 263 * 264 * // foo.not(val) will return val 265 * spyOn(foo, 'not').andCallFake(function(value) {return value;}); 266 * 267 * // mock example 268 * foo.not(7 == 7); 269 * expect(foo.not).toHaveBeenCalled(); 270 * expect(foo.not).toHaveBeenCalledWith(true); 271 * 272 * @constructor 273 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj 274 * @param {String} name 275 */ 276 jasmine.Spy = function(name) { 277 /** 278 * The name of the spy, if provided. 279 */ 280 this.identity = name || 'unknown'; 281 /** 282 * Is this Object a spy? 283 */ 284 this.isSpy = true; 285 /** 286 * The actual function this spy stubs. 287 */ 288 this.plan = function() { 289 }; 290 /** 291 * Tracking of the most recent call to the spy. 292 * @example 293 * var mySpy = jasmine.createSpy('foo'); 294 * mySpy(1, 2); 295 * mySpy.mostRecentCall.args = [1, 2]; 296 */ 297 this.mostRecentCall = {}; 298 299 /** 300 * Holds arguments for each call to the spy, indexed by call count 301 * @example 302 * var mySpy = jasmine.createSpy('foo'); 303 * mySpy(1, 2); 304 * mySpy(7, 8); 305 * mySpy.mostRecentCall.args = [7, 8]; 306 * mySpy.argsForCall[0] = [1, 2]; 307 * mySpy.argsForCall[1] = [7, 8]; 308 */ 309 this.argsForCall = []; 310 this.calls = []; 311 }; 312 313 /** 314 * Tells a spy to call through to the actual implemenatation. 315 * 316 * @example 317 * var foo = { 318 * bar: function() { // do some stuff } 319 * } 320 * 321 * // defining a spy on an existing property: foo.bar 322 * spyOn(foo, 'bar').andCallThrough(); 323 */ 324 jasmine.Spy.prototype.andCallThrough = function() { 325 this.plan = this.originalValue; 326 return this; 327 }; 328 329 /** 330 * For setting the return value of a spy. 331 * 332 * @example 333 * // defining a spy from scratch: foo() returns 'baz' 334 * var foo = jasmine.createSpy('spy on foo').andReturn('baz'); 335 * 336 * // defining a spy on an existing property: foo.bar() returns 'baz' 337 * spyOn(foo, 'bar').andReturn('baz'); 338 * 339 * @param {Object} value 340 */ 341 jasmine.Spy.prototype.andReturn = function(value) { 342 this.plan = function() { 343 return value; 344 }; 345 return this; 346 }; 347 348 /** 349 * For throwing an exception when a spy is called. 350 * 351 * @example 352 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch' 353 * var foo = jasmine.createSpy('spy on foo').andThrow('baz'); 354 * 355 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch' 356 * spyOn(foo, 'bar').andThrow('baz'); 357 * 358 * @param {String} exceptionMsg 359 */ 360 jasmine.Spy.prototype.andThrow = function(exceptionMsg) { 361 this.plan = function() { 362 throw exceptionMsg; 363 }; 364 return this; 365 }; 366 367 /** 368 * Calls an alternate implementation when a spy is called. 369 * 370 * @example 371 * var baz = function() { 372 * // do some stuff, return something 373 * } 374 * // defining a spy from scratch: foo() calls the function baz 375 * var foo = jasmine.createSpy('spy on foo').andCall(baz); 376 * 377 * // defining a spy on an existing property: foo.bar() calls an anonymnous function 378 * spyOn(foo, 'bar').andCall(function() { return 'baz';} ); 379 * 380 * @param {Function} fakeFunc 381 */ 382 jasmine.Spy.prototype.andCallFake = function(fakeFunc) { 383 this.plan = fakeFunc; 384 return this; 385 }; 386 387 /** 388 * Resets all of a spy's the tracking variables so that it can be used again. 389 * 390 * @example 391 * spyOn(foo, 'bar'); 392 * 393 * foo.bar(); 394 * 395 * expect(foo.bar.callCount).toEqual(1); 396 * 397 * foo.bar.reset(); 398 * 399 * expect(foo.bar.callCount).toEqual(0); 400 */ 401 jasmine.Spy.prototype.reset = function() { 402 this.wasCalled = false; 403 this.callCount = 0; 404 this.argsForCall = []; 405 this.calls = []; 406 this.mostRecentCall = {}; 407 }; 408 409 jasmine.createSpy = function(name) { 410 411 var spyObj = function() { 412 spyObj.wasCalled = true; 413 spyObj.callCount++; 414 var args = jasmine.util.argsToArray(arguments); 415 spyObj.mostRecentCall.object = this; 416 spyObj.mostRecentCall.args = args; 417 spyObj.argsForCall.push(args); 418 spyObj.calls.push({object: this, args: args}); 419 return spyObj.plan.apply(this, arguments); 420 }; 421 422 var spy = new jasmine.Spy(name); 423 424 for (var prop in spy) { 425 spyObj[prop] = spy[prop]; 426 } 427 428 spyObj.reset(); 429 430 return spyObj; 431 }; 432 433 /** 434 * Determines whether an object is a spy. 435 * 436 * @param {jasmine.Spy|Object} putativeSpy 437 * @returns {Boolean} 438 */ 439 jasmine.isSpy = function(putativeSpy) { 440 return putativeSpy && putativeSpy.isSpy; 441 }; 442 443 /** 444 * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something 445 * large in one call. 446 * 447 * @param {String} baseName name of spy class 448 * @param {Array} methodNames array of names of methods to make spies 449 */ 450 jasmine.createSpyObj = function(baseName, methodNames) { 451 if (!jasmine.isArray_(methodNames) || methodNames.length === 0) { 452 throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); 453 } 454 var obj = {}; 455 for (var i = 0; i < methodNames.length; i++) { 456 obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); 457 } 458 return obj; 459 }; 460 461 /** 462 * All parameters are pretty-printed and concatenated together, then written to the current spec's output. 463 * 464 * Be careful not to leave calls to <code>jasmine.log</code> in production code. 465 */ 466 jasmine.log = function() { 467 var spec = jasmine.getEnv().currentSpec; 468 spec.log.apply(spec, arguments); 469 }; 470 471 /** 472 * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy. 473 * 474 * @example 475 * // spy example 476 * var foo = { 477 * not: function(bool) { return !bool; } 478 * } 479 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops 480 * 481 * @see jasmine.createSpy 482 * @param obj 483 * @param methodName 484 * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods 485 */ 486 var spyOn = function(obj, methodName) { 487 return jasmine.getEnv().currentSpec.spyOn(obj, methodName); 488 }; 489 if (isCommonJS) exports.spyOn = spyOn; 490 491 /** 492 * Creates a Jasmine spec that will be added to the current suite. 493 * 494 * // TODO: pending tests 495 * 496 * @example 497 * it('should be true', function() { 498 * expect(true).toEqual(true); 499 * }); 500 * 501 * @param {String} desc description of this specification 502 * @param {Function} func defines the preconditions and expectations of the spec 503 */ 504 var it = function(desc, func) { 505 return jasmine.getEnv().it(desc, func); 506 }; 507 if (isCommonJS) exports.it = it; 508 509 /** 510 * Creates a <em>disabled</em> Jasmine spec. 511 * 512 * A convenience method that allows existing specs to be disabled temporarily during development. 513 * 514 * @param {String} desc description of this specification 515 * @param {Function} func defines the preconditions and expectations of the spec 516 */ 517 var xit = function(desc, func) { 518 return jasmine.getEnv().xit(desc, func); 519 }; 520 if (isCommonJS) exports.xit = xit; 521 522 /** 523 * Starts a chain for a Jasmine expectation. 524 * 525 * It is passed an Object that is the actual value and should chain to one of the many 526 * jasmine.Matchers functions. 527 * 528 * @param {Object} actual Actual value to test against and expected value 529 * @return {jasmine.Matchers} 530 */ 531 var expect = function(actual) { 532 return jasmine.getEnv().currentSpec.expect(actual); 533 }; 534 if (isCommonJS) exports.expect = expect; 535 536 /** 537 * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs. 538 * 539 * @param {Function} func Function that defines part of a jasmine spec. 540 */ 541 var runs = function(func) { 542 jasmine.getEnv().currentSpec.runs(func); 543 }; 544 if (isCommonJS) exports.runs = runs; 545 546 /** 547 * Waits a fixed time period before moving to the next block. 548 * 549 * @deprecated Use waitsFor() instead 550 * @param {Number} timeout milliseconds to wait 551 */ 552 var waits = function(timeout) { 553 jasmine.getEnv().currentSpec.waits(timeout); 554 }; 555 if (isCommonJS) exports.waits = waits; 556 557 /** 558 * Waits for the latchFunction to return true before proceeding to the next block. 559 * 560 * @param {Function} latchFunction 561 * @param {String} optional_timeoutMessage 562 * @param {Number} optional_timeout 563 */ 564 var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 565 jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments); 566 }; 567 if (isCommonJS) exports.waitsFor = waitsFor; 568 569 /** 570 * A function that is called before each spec in a suite. 571 * 572 * Used for spec setup, including validating assumptions. 573 * 574 * @param {Function} beforeEachFunction 575 */ 576 var beforeEach = function(beforeEachFunction) { 577 jasmine.getEnv().beforeEach(beforeEachFunction); 578 }; 579 if (isCommonJS) exports.beforeEach = beforeEach; 580 581 /** 582 * A function that is called after each spec in a suite. 583 * 584 * Used for restoring any state that is hijacked during spec execution. 585 * 586 * @param {Function} afterEachFunction 587 */ 588 var afterEach = function(afterEachFunction) { 589 jasmine.getEnv().afterEach(afterEachFunction); 590 }; 591 if (isCommonJS) exports.afterEach = afterEach; 592 593 /** 594 * Defines a suite of specifications. 595 * 596 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared 597 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization 598 * of setup in some tests. 599 * 600 * @example 601 * // TODO: a simple suite 602 * 603 * // TODO: a simple suite with a nested describe block 604 * 605 * @param {String} description A string, usually the class under test. 606 * @param {Function} specDefinitions function that defines several specs. 607 */ 608 var describe = function(description, specDefinitions) { 609 return jasmine.getEnv().describe(description, specDefinitions); 610 }; 611 if (isCommonJS) exports.describe = describe; 612 613 /** 614 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development. 615 * 616 * @param {String} description A string, usually the class under test. 617 * @param {Function} specDefinitions function that defines several specs. 618 */ 619 var xdescribe = function(description, specDefinitions) { 620 return jasmine.getEnv().xdescribe(description, specDefinitions); 621 }; 622 if (isCommonJS) exports.xdescribe = xdescribe; 623 624 625 // Provide the XMLHttpRequest class for IE 5.x-6.x: 626 jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() { 627 function tryIt(f) { 628 try { 629 return f(); 630 } catch(e) { 631 } 632 return null; 633 } 634 635 var xhr = tryIt(function() { 636 return new ActiveXObject("Msxml2.XMLHTTP.6.0"); 637 }) || 638 tryIt(function() { 639 return new ActiveXObject("Msxml2.XMLHTTP.3.0"); 640 }) || 641 tryIt(function() { 642 return new ActiveXObject("Msxml2.XMLHTTP"); 643 }) || 644 tryIt(function() { 645 return new ActiveXObject("Microsoft.XMLHTTP"); 646 }); 647 648 if (!xhr) throw new Error("This browser does not support XMLHttpRequest."); 649 650 return xhr; 651 } : XMLHttpRequest; 652 /** 653 * @namespace 654 */ 655 jasmine.util = {}; 656 657 /** 658 * Declare that a child class inherit it's prototype from the parent class. 659 * 660 * @private 661 * @param {Function} childClass 662 * @param {Function} parentClass 663 */ 664 jasmine.util.inherit = function(childClass, parentClass) { 665 /** 666 * @private 667 */ 668 var subclass = function() { 669 }; 670 subclass.prototype = parentClass.prototype; 671 childClass.prototype = new subclass(); 672 }; 673 674 jasmine.util.formatException = function(e) { 675 var lineNumber; 676 if (e.line) { 677 lineNumber = e.line; 678 } 679 else if (e.lineNumber) { 680 lineNumber = e.lineNumber; 681 } 682 683 var file; 684 685 if (e.sourceURL) { 686 file = e.sourceURL; 687 } 688 else if (e.fileName) { 689 file = e.fileName; 690 } 691 692 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString(); 693 694 if (file && lineNumber) { 695 message += ' in ' + file + ' (line ' + lineNumber + ')'; 696 } 697 698 return message; 699 }; 700 701 jasmine.util.htmlEscape = function(str) { 702 if (!str) return str; 703 return str.replace(/&/g, '&') 704 .replace(/</g, '<') 705 .replace(/>/g, '>'); 706 }; 707 708 jasmine.util.argsToArray = function(args) { 709 var arrayOfArgs = []; 710 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]); 711 return arrayOfArgs; 712 }; 713 714 jasmine.util.extend = function(destination, source) { 715 for (var property in source) destination[property] = source[property]; 716 return destination; 717 }; 718 719 /** 720 * Environment for Jasmine 721 * 722 * @constructor 723 */ 724 jasmine.Env = function() { 725 this.currentSpec = null; 726 this.currentSuite = null; 727 this.currentRunner_ = new jasmine.Runner(this); 728 729 this.reporter = new jasmine.MultiReporter(); 730 731 this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL; 732 this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL; 733 this.lastUpdate = 0; 734 this.specFilter = function() { 735 return true; 736 }; 737 738 this.nextSpecId_ = 0; 739 this.nextSuiteId_ = 0; 740 this.equalityTesters_ = []; 741 742 // wrap matchers 743 this.matchersClass = function() { 744 jasmine.Matchers.apply(this, arguments); 745 }; 746 jasmine.util.inherit(this.matchersClass, jasmine.Matchers); 747 748 jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass); 749 }; 750 751 752 jasmine.Env.prototype.setTimeout = jasmine.setTimeout; 753 jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout; 754 jasmine.Env.prototype.setInterval = jasmine.setInterval; 755 jasmine.Env.prototype.clearInterval = jasmine.clearInterval; 756 757 /** 758 * @returns an object containing jasmine version build info, if set. 759 */ 760 jasmine.Env.prototype.version = function () { 761 if (jasmine.version_) { 762 return jasmine.version_; 763 } else { 764 throw new Error('Version not set'); 765 } 766 }; 767 768 /** 769 * @returns string containing jasmine version build info, if set. 770 */ 771 jasmine.Env.prototype.versionString = function() { 772 if (!jasmine.version_) { 773 return "version unknown"; 774 } 775 776 var version = this.version(); 777 var versionString = version.major + "." + version.minor + "." + version.build; 778 if (version.release_candidate) { 779 versionString += ".rc" + version.release_candidate; 780 } 781 versionString += " revision " + version.revision; 782 return versionString; 783 }; 784 785 /** 786 * @returns a sequential integer starting at 0 787 */ 788 jasmine.Env.prototype.nextSpecId = function () { 789 return this.nextSpecId_++; 790 }; 791 792 /** 793 * @returns a sequential integer starting at 0 794 */ 795 jasmine.Env.prototype.nextSuiteId = function () { 796 return this.nextSuiteId_++; 797 }; 798 799 /** 800 * Register a reporter to receive status updates from Jasmine. 801 * @param {jasmine.Reporter} reporter An object which will receive status updates. 802 */ 803 jasmine.Env.prototype.addReporter = function(reporter) { 804 this.reporter.addReporter(reporter); 805 }; 806 807 jasmine.Env.prototype.execute = function() { 808 this.currentRunner_.execute(); 809 }; 810 811 jasmine.Env.prototype.describe = function(description, specDefinitions) { 812 var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite); 813 814 var parentSuite = this.currentSuite; 815 if (parentSuite) { 816 parentSuite.add(suite); 817 } else { 818 this.currentRunner_.add(suite); 819 } 820 821 this.currentSuite = suite; 822 823 var declarationError = null; 824 try { 825 specDefinitions.call(suite); 826 } catch(e) { 827 declarationError = e; 828 } 829 830 if (declarationError) { 831 this.it("encountered a declaration exception", function() { 832 throw declarationError; 833 }); 834 } 835 836 this.currentSuite = parentSuite; 837 838 return suite; 839 }; 840 841 jasmine.Env.prototype.beforeEach = function(beforeEachFunction) { 842 if (this.currentSuite) { 843 this.currentSuite.beforeEach(beforeEachFunction); 844 } else { 845 this.currentRunner_.beforeEach(beforeEachFunction); 846 } 847 }; 848 849 jasmine.Env.prototype.currentRunner = function () { 850 return this.currentRunner_; 851 }; 852 853 jasmine.Env.prototype.afterEach = function(afterEachFunction) { 854 if (this.currentSuite) { 855 this.currentSuite.afterEach(afterEachFunction); 856 } else { 857 this.currentRunner_.afterEach(afterEachFunction); 858 } 859 860 }; 861 862 jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) { 863 return { 864 execute: function() { 865 } 866 }; 867 }; 868 869 jasmine.Env.prototype.it = function(description, func) { 870 var spec = new jasmine.Spec(this, this.currentSuite, description); 871 this.currentSuite.add(spec); 872 this.currentSpec = spec; 873 874 if (func) { 875 spec.runs(func); 876 } 877 878 return spec; 879 }; 880 881 jasmine.Env.prototype.xit = function(desc, func) { 882 return { 883 id: this.nextSpecId(), 884 runs: function() { 885 } 886 }; 887 }; 888 889 jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) { 890 if (a.source != b.source) 891 mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/"); 892 893 if (a.ignoreCase != b.ignoreCase) 894 mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier"); 895 896 if (a.global != b.global) 897 mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier"); 898 899 if (a.multiline != b.multiline) 900 mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier"); 901 902 if (a.sticky != b.sticky) 903 mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier"); 904 905 return (mismatchValues.length === 0); 906 }; 907 908 jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) { 909 if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) { 910 return true; 911 } 912 913 a.__Jasmine_been_here_before__ = b; 914 b.__Jasmine_been_here_before__ = a; 915 916 var hasKey = function(obj, keyName) { 917 return obj !== null && obj[keyName] !== jasmine.undefined; 918 }; 919 920 for (var property in b) { 921 if (!hasKey(a, property) && hasKey(b, property)) { 922 mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 923 } 924 } 925 for (property in a) { 926 if (!hasKey(b, property) && hasKey(a, property)) { 927 mismatchKeys.push("expected missing key '" + property + "', but present in actual."); 928 } 929 } 930 for (property in b) { 931 if (property == '__Jasmine_been_here_before__') continue; 932 if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) { 933 mismatchValues.push("'" + property + "' was '" + (b[property] ? jasmine.util.htmlEscape(b[property].toString()) : b[property]) + "' in expected, but was '" + (a[property] ? jasmine.util.htmlEscape(a[property].toString()) : a[property]) + "' in actual."); 934 } 935 } 936 937 if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) { 938 mismatchValues.push("arrays were not the same length"); 939 } 940 941 delete a.__Jasmine_been_here_before__; 942 delete b.__Jasmine_been_here_before__; 943 return (mismatchKeys.length === 0 && mismatchValues.length === 0); 944 }; 945 946 jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) { 947 mismatchKeys = mismatchKeys || []; 948 mismatchValues = mismatchValues || []; 949 950 for (var i = 0; i < this.equalityTesters_.length; i++) { 951 var equalityTester = this.equalityTesters_[i]; 952 var result = equalityTester(a, b, this, mismatchKeys, mismatchValues); 953 if (result !== jasmine.undefined) return result; 954 } 955 956 if (a === b) return true; 957 958 if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) { 959 return (a == jasmine.undefined && b == jasmine.undefined); 960 } 961 962 if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) { 963 return a === b; 964 } 965 966 if (a instanceof Date && b instanceof Date) { 967 return a.getTime() == b.getTime(); 968 } 969 970 if (a.jasmineMatches) { 971 return a.jasmineMatches(b); 972 } 973 974 if (b.jasmineMatches) { 975 return b.jasmineMatches(a); 976 } 977 978 if (a instanceof jasmine.Matchers.ObjectContaining) { 979 return a.matches(b); 980 } 981 982 if (b instanceof jasmine.Matchers.ObjectContaining) { 983 return b.matches(a); 984 } 985 986 if (jasmine.isString_(a) && jasmine.isString_(b)) { 987 return (a == b); 988 } 989 990 if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) { 991 return (a == b); 992 } 993 994 if (a instanceof RegExp && b instanceof RegExp) { 995 return this.compareRegExps_(a, b, mismatchKeys, mismatchValues); 996 } 997 998 if (typeof a === "object" && typeof b === "object") { 999 return this.compareObjects_(a, b, mismatchKeys, mismatchValues); 1000 } 1001 1002 //Straight check 1003 return (a === b); 1004 }; 1005 1006 jasmine.Env.prototype.contains_ = function(haystack, needle) { 1007 if (jasmine.isArray_(haystack)) { 1008 for (var i = 0; i < haystack.length; i++) { 1009 if (this.equals_(haystack[i], needle)) return true; 1010 } 1011 return false; 1012 } 1013 return haystack.indexOf(needle) >= 0; 1014 }; 1015 1016 jasmine.Env.prototype.addEqualityTester = function(equalityTester) { 1017 this.equalityTesters_.push(equalityTester); 1018 }; 1019 /** No-op base class for Jasmine reporters. 1020 * 1021 * @constructor 1022 */ 1023 jasmine.Reporter = function() { 1024 }; 1025 1026 //noinspection JSUnusedLocalSymbols 1027 jasmine.Reporter.prototype.reportRunnerStarting = function(runner) { 1028 }; 1029 1030 //noinspection JSUnusedLocalSymbols 1031 jasmine.Reporter.prototype.reportRunnerResults = function(runner) { 1032 }; 1033 1034 //noinspection JSUnusedLocalSymbols 1035 jasmine.Reporter.prototype.reportSuiteResults = function(suite) { 1036 }; 1037 1038 //noinspection JSUnusedLocalSymbols 1039 jasmine.Reporter.prototype.reportSpecStarting = function(spec) { 1040 }; 1041 1042 //noinspection JSUnusedLocalSymbols 1043 jasmine.Reporter.prototype.reportSpecResults = function(spec) { 1044 }; 1045 1046 //noinspection JSUnusedLocalSymbols 1047 jasmine.Reporter.prototype.log = function(str) { 1048 }; 1049 1050 /** 1051 * Blocks are functions with executable code that make up a spec. 1052 * 1053 * @constructor 1054 * @param {jasmine.Env} env 1055 * @param {Function} func 1056 * @param {jasmine.Spec} spec 1057 */ 1058 jasmine.Block = function(env, func, spec) { 1059 this.env = env; 1060 this.func = func; 1061 this.spec = spec; 1062 }; 1063 1064 jasmine.Block.prototype.execute = function(onComplete) { 1065 if (!jasmine.CATCH_EXCEPTIONS) { 1066 this.func.apply(this.spec); 1067 } 1068 else { 1069 try { 1070 this.func.apply(this.spec); 1071 } catch (e) { 1072 this.spec.fail(e); 1073 } 1074 } 1075 onComplete(); 1076 }; 1077 /** JavaScript API reporter. 1078 * 1079 * @constructor 1080 */ 1081 jasmine.JsApiReporter = function() { 1082 this.started = false; 1083 this.finished = false; 1084 this.suites_ = []; 1085 this.results_ = {}; 1086 }; 1087 1088 jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) { 1089 this.started = true; 1090 var suites = runner.topLevelSuites(); 1091 for (var i = 0; i < suites.length; i++) { 1092 var suite = suites[i]; 1093 this.suites_.push(this.summarize_(suite)); 1094 } 1095 }; 1096 1097 jasmine.JsApiReporter.prototype.suites = function() { 1098 return this.suites_; 1099 }; 1100 1101 jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) { 1102 var isSuite = suiteOrSpec instanceof jasmine.Suite; 1103 var summary = { 1104 id: suiteOrSpec.id, 1105 name: suiteOrSpec.description, 1106 type: isSuite ? 'suite' : 'spec', 1107 children: [] 1108 }; 1109 1110 if (isSuite) { 1111 var children = suiteOrSpec.children(); 1112 for (var i = 0; i < children.length; i++) { 1113 summary.children.push(this.summarize_(children[i])); 1114 } 1115 } 1116 return summary; 1117 }; 1118 1119 jasmine.JsApiReporter.prototype.results = function() { 1120 return this.results_; 1121 }; 1122 1123 jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) { 1124 return this.results_[specId]; 1125 }; 1126 1127 //noinspection JSUnusedLocalSymbols 1128 jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) { 1129 this.finished = true; 1130 }; 1131 1132 //noinspection JSUnusedLocalSymbols 1133 jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) { 1134 }; 1135 1136 //noinspection JSUnusedLocalSymbols 1137 jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) { 1138 this.results_[spec.id] = { 1139 messages: spec.results().getItems(), 1140 result: spec.results().failedCount > 0 ? "failed" : "passed" 1141 }; 1142 }; 1143 1144 //noinspection JSUnusedLocalSymbols 1145 jasmine.JsApiReporter.prototype.log = function(str) { 1146 }; 1147 1148 jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){ 1149 var results = {}; 1150 for (var i = 0; i < specIds.length; i++) { 1151 var specId = specIds[i]; 1152 results[specId] = this.summarizeResult_(this.results_[specId]); 1153 } 1154 return results; 1155 }; 1156 1157 jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ 1158 var summaryMessages = []; 1159 var messagesLength = result.messages.length; 1160 for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) { 1161 var resultMessage = result.messages[messageIndex]; 1162 summaryMessages.push({ 1163 text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined, 1164 passed: resultMessage.passed ? resultMessage.passed() : true, 1165 type: resultMessage.type, 1166 message: resultMessage.message, 1167 trace: { 1168 stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined 1169 } 1170 }); 1171 } 1172 1173 return { 1174 result : result.result, 1175 messages : summaryMessages 1176 }; 1177 }; 1178 1179 /** 1180 * @constructor 1181 * @param {jasmine.Env} env 1182 * @param actual 1183 * @param {jasmine.Spec} spec 1184 */ 1185 jasmine.Matchers = function(env, actual, spec, opt_isNot) { 1186 this.env = env; 1187 this.actual = actual; 1188 this.spec = spec; 1189 this.isNot = opt_isNot || false; 1190 this.reportWasCalled_ = false; 1191 }; 1192 1193 // todo: @deprecated as of Jasmine 0.11, remove soon [xw] 1194 jasmine.Matchers.pp = function(str) { 1195 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!"); 1196 }; 1197 1198 // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw] 1199 jasmine.Matchers.prototype.report = function(result, failing_message, details) { 1200 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); 1201 }; 1202 1203 jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) { 1204 for (var methodName in prototype) { 1205 if (methodName == 'report') continue; 1206 var orig = prototype[methodName]; 1207 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig); 1208 } 1209 }; 1210 1211 jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { 1212 return function() { 1213 var matcherArgs = jasmine.util.argsToArray(arguments); 1214 var result = matcherFunction.apply(this, arguments); 1215 1216 if (this.isNot) { 1217 result = !result; 1218 } 1219 1220 if (this.reportWasCalled_) return result; 1221 1222 var message; 1223 if (!result) { 1224 if (this.message) { 1225 message = this.message.apply(this, arguments); 1226 if (jasmine.isArray_(message)) { 1227 message = message[this.isNot ? 1 : 0]; 1228 } 1229 } else { 1230 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); 1231 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; 1232 if (matcherArgs.length > 0) { 1233 for (var i = 0; i < matcherArgs.length; i++) { 1234 if (i > 0) message += ","; 1235 message += " " + jasmine.pp(matcherArgs[i]); 1236 } 1237 } 1238 message += "."; 1239 } 1240 } 1241 var expectationResult = new jasmine.ExpectationResult({ 1242 matcherName: matcherName, 1243 passed: result, 1244 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0], 1245 actual: this.actual, 1246 message: message 1247 }); 1248 this.spec.addMatcherResult(expectationResult); 1249 return jasmine.undefined; 1250 }; 1251 }; 1252 1253 1254 1255 1256 /** 1257 * toBe: compares the actual to the expected using === 1258 * @param expected 1259 */ 1260 jasmine.Matchers.prototype.toBe = function(expected) { 1261 return this.actual === expected; 1262 }; 1263 1264 /** 1265 * toNotBe: compares the actual to the expected using !== 1266 * @param expected 1267 * @deprecated as of 1.0. Use not.toBe() instead. 1268 */ 1269 jasmine.Matchers.prototype.toNotBe = function(expected) { 1270 return this.actual !== expected; 1271 }; 1272 1273 /** 1274 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc. 1275 * 1276 * @param expected 1277 */ 1278 jasmine.Matchers.prototype.toEqual = function(expected) { 1279 return this.env.equals_(this.actual, expected); 1280 }; 1281 1282 /** 1283 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual 1284 * @param expected 1285 * @deprecated as of 1.0. Use not.toEqual() instead. 1286 */ 1287 jasmine.Matchers.prototype.toNotEqual = function(expected) { 1288 return !this.env.equals_(this.actual, expected); 1289 }; 1290 1291 /** 1292 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes 1293 * a pattern or a String. 1294 * 1295 * @param expected 1296 */ 1297 jasmine.Matchers.prototype.toMatch = function(expected) { 1298 return new RegExp(expected).test(this.actual); 1299 }; 1300 1301 /** 1302 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch 1303 * @param expected 1304 * @deprecated as of 1.0. Use not.toMatch() instead. 1305 */ 1306 jasmine.Matchers.prototype.toNotMatch = function(expected) { 1307 return !(new RegExp(expected).test(this.actual)); 1308 }; 1309 1310 /** 1311 * Matcher that compares the actual to jasmine.undefined. 1312 */ 1313 jasmine.Matchers.prototype.toBeDefined = function() { 1314 return (this.actual !== jasmine.undefined); 1315 }; 1316 1317 /** 1318 * Matcher that compares the actual to jasmine.undefined. 1319 */ 1320 jasmine.Matchers.prototype.toBeUndefined = function() { 1321 return (this.actual === jasmine.undefined); 1322 }; 1323 1324 /** 1325 * Matcher that compares the actual to null. 1326 */ 1327 jasmine.Matchers.prototype.toBeNull = function() { 1328 return (this.actual === null); 1329 }; 1330 1331 /** 1332 * Matcher that compares the actual to NaN. 1333 */ 1334 jasmine.Matchers.prototype.toBeNaN = function() { 1335 this.message = function() { 1336 return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ]; 1337 }; 1338 1339 return (this.actual !== this.actual); 1340 }; 1341 1342 /** 1343 * Matcher that boolean not-nots the actual. 1344 */ 1345 jasmine.Matchers.prototype.toBeTruthy = function() { 1346 return !!this.actual; 1347 }; 1348 1349 1350 /** 1351 * Matcher that boolean nots the actual. 1352 */ 1353 jasmine.Matchers.prototype.toBeFalsy = function() { 1354 return !this.actual; 1355 }; 1356 1357 1358 /** 1359 * Matcher that checks to see if the actual, a Jasmine spy, was called. 1360 */ 1361 jasmine.Matchers.prototype.toHaveBeenCalled = function() { 1362 if (arguments.length > 0) { 1363 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith'); 1364 } 1365 1366 if (!jasmine.isSpy(this.actual)) { 1367 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1368 } 1369 1370 this.message = function() { 1371 return [ 1372 "Expected spy " + this.actual.identity + " to have been called.", 1373 "Expected spy " + this.actual.identity + " not to have been called." 1374 ]; 1375 }; 1376 1377 return this.actual.wasCalled; 1378 }; 1379 1380 /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */ 1381 jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled; 1382 1383 /** 1384 * Matcher that checks to see if the actual, a Jasmine spy, was not called. 1385 * 1386 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead 1387 */ 1388 jasmine.Matchers.prototype.wasNotCalled = function() { 1389 if (arguments.length > 0) { 1390 throw new Error('wasNotCalled does not take arguments'); 1391 } 1392 1393 if (!jasmine.isSpy(this.actual)) { 1394 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1395 } 1396 1397 this.message = function() { 1398 return [ 1399 "Expected spy " + this.actual.identity + " to not have been called.", 1400 "Expected spy " + this.actual.identity + " to have been called." 1401 ]; 1402 }; 1403 1404 return !this.actual.wasCalled; 1405 }; 1406 1407 /** 1408 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters. 1409 * 1410 * @example 1411 * 1412 */ 1413 jasmine.Matchers.prototype.toHaveBeenCalledWith = function() { 1414 var expectedArgs = jasmine.util.argsToArray(arguments); 1415 if (!jasmine.isSpy(this.actual)) { 1416 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1417 } 1418 this.message = function() { 1419 var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was."; 1420 var positiveMessage = ""; 1421 if (this.actual.callCount === 0) { 1422 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; 1423 } else { 1424 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but actual calls were " + jasmine.pp(this.actual.argsForCall).replace(/^\[ | \]$/g, '') 1425 } 1426 return [positiveMessage, invertedMessage]; 1427 }; 1428 1429 return this.env.contains_(this.actual.argsForCall, expectedArgs); 1430 }; 1431 1432 /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */ 1433 jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith; 1434 1435 /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */ 1436 jasmine.Matchers.prototype.wasNotCalledWith = function() { 1437 var expectedArgs = jasmine.util.argsToArray(arguments); 1438 if (!jasmine.isSpy(this.actual)) { 1439 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.'); 1440 } 1441 1442 this.message = function() { 1443 return [ 1444 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was", 1445 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was" 1446 ]; 1447 }; 1448 1449 return !this.env.contains_(this.actual.argsForCall, expectedArgs); 1450 }; 1451 1452 /** 1453 * Matcher that checks that the expected item is an element in the actual Array. 1454 * 1455 * @param {Object} expected 1456 */ 1457 jasmine.Matchers.prototype.toContain = function(expected) { 1458 return this.env.contains_(this.actual, expected); 1459 }; 1460 1461 /** 1462 * Matcher that checks that the expected item is NOT an element in the actual Array. 1463 * 1464 * @param {Object} expected 1465 * @deprecated as of 1.0. Use not.toContain() instead. 1466 */ 1467 jasmine.Matchers.prototype.toNotContain = function(expected) { 1468 return !this.env.contains_(this.actual, expected); 1469 }; 1470 1471 jasmine.Matchers.prototype.toBeLessThan = function(expected) { 1472 return this.actual < expected; 1473 }; 1474 1475 jasmine.Matchers.prototype.toBeGreaterThan = function(expected) { 1476 return this.actual > expected; 1477 }; 1478 1479 /** 1480 * Matcher that checks that the expected item is equal to the actual item 1481 * up to a given level of decimal precision (default 2). 1482 * 1483 * @param {Number} expected 1484 * @param {Number} precision, as number of decimal places 1485 */ 1486 jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) { 1487 if (!(precision === 0)) { 1488 precision = precision || 2; 1489 } 1490 return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2); 1491 }; 1492 1493 /** 1494 * Matcher that checks that the expected exception was thrown by the actual. 1495 * 1496 * @param {String} [expected] 1497 */ 1498 jasmine.Matchers.prototype.toThrow = function(expected) { 1499 var result = false; 1500 var exception; 1501 if (typeof this.actual != 'function') { 1502 throw new Error('Actual is not a function'); 1503 } 1504 try { 1505 this.actual(); 1506 } catch (e) { 1507 exception = e; 1508 } 1509 if (exception) { 1510 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected)); 1511 } 1512 1513 var not = this.isNot ? "not " : ""; 1514 1515 this.message = function() { 1516 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 1517 return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' '); 1518 } else { 1519 return "Expected function to throw an exception."; 1520 } 1521 }; 1522 1523 return result; 1524 }; 1525 1526 jasmine.Matchers.Any = function(expectedClass) { 1527 this.expectedClass = expectedClass; 1528 }; 1529 1530 jasmine.Matchers.Any.prototype.jasmineMatches = function(other) { 1531 if (this.expectedClass == String) { 1532 return typeof other == 'string' || other instanceof String; 1533 } 1534 1535 if (this.expectedClass == Number) { 1536 return typeof other == 'number' || other instanceof Number; 1537 } 1538 1539 if (this.expectedClass == Function) { 1540 return typeof other == 'function' || other instanceof Function; 1541 } 1542 1543 if (this.expectedClass == Object) { 1544 return typeof other == 'object'; 1545 } 1546 1547 return other instanceof this.expectedClass; 1548 }; 1549 1550 jasmine.Matchers.Any.prototype.jasmineToString = function() { 1551 return '<jasmine.any(' + this.expectedClass + ')>'; 1552 }; 1553 1554 jasmine.Matchers.ObjectContaining = function (sample) { 1555 this.sample = sample; 1556 }; 1557 1558 jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) { 1559 mismatchKeys = mismatchKeys || []; 1560 mismatchValues = mismatchValues || []; 1561 1562 var env = jasmine.getEnv(); 1563 1564 var hasKey = function(obj, keyName) { 1565 return obj != null && obj[keyName] !== jasmine.undefined; 1566 }; 1567 1568 for (var property in this.sample) { 1569 if (!hasKey(other, property) && hasKey(this.sample, property)) { 1570 mismatchKeys.push("expected has key '" + property + "', but missing from actual."); 1571 } 1572 else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) { 1573 mismatchValues.push("'" + property + "' was '" + (other[property] ? jasmine.util.htmlEscape(other[property].toString()) : other[property]) + "' in expected, but was '" + (this.sample[property] ? jasmine.util.htmlEscape(this.sample[property].toString()) : this.sample[property]) + "' in actual."); 1574 } 1575 } 1576 1577 return (mismatchKeys.length === 0 && mismatchValues.length === 0); 1578 }; 1579 1580 jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () { 1581 return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>"; 1582 }; 1583 // Mock setTimeout, clearTimeout 1584 // Contributed by Pivotal Computer Systems, www.pivotalsf.com 1585 1586 jasmine.FakeTimer = function() { 1587 this.reset(); 1588 1589 var self = this; 1590 self.setTimeout = function(funcToCall, millis) { 1591 self.timeoutsMade++; 1592 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false); 1593 return self.timeoutsMade; 1594 }; 1595 1596 self.setInterval = function(funcToCall, millis) { 1597 self.timeoutsMade++; 1598 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true); 1599 return self.timeoutsMade; 1600 }; 1601 1602 self.clearTimeout = function(timeoutKey) { 1603 self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1604 }; 1605 1606 self.clearInterval = function(timeoutKey) { 1607 self.scheduledFunctions[timeoutKey] = jasmine.undefined; 1608 }; 1609 1610 }; 1611 1612 jasmine.FakeTimer.prototype.reset = function() { 1613 this.timeoutsMade = 0; 1614 this.scheduledFunctions = {}; 1615 this.nowMillis = 0; 1616 }; 1617 1618 jasmine.FakeTimer.prototype.tick = function(millis) { 1619 var oldMillis = this.nowMillis; 1620 var newMillis = oldMillis + millis; 1621 this.runFunctionsWithinRange(oldMillis, newMillis); 1622 this.nowMillis = newMillis; 1623 }; 1624 1625 jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) { 1626 var scheduledFunc; 1627 var funcsToRun = []; 1628 for (var timeoutKey in this.scheduledFunctions) { 1629 scheduledFunc = this.scheduledFunctions[timeoutKey]; 1630 if (scheduledFunc != jasmine.undefined && 1631 scheduledFunc.runAtMillis >= oldMillis && 1632 scheduledFunc.runAtMillis <= nowMillis) { 1633 funcsToRun.push(scheduledFunc); 1634 this.scheduledFunctions[timeoutKey] = jasmine.undefined; 1635 } 1636 } 1637 1638 if (funcsToRun.length > 0) { 1639 funcsToRun.sort(function(a, b) { 1640 return a.runAtMillis - b.runAtMillis; 1641 }); 1642 for (var i = 0; i < funcsToRun.length; ++i) { 1643 try { 1644 var funcToRun = funcsToRun[i]; 1645 this.nowMillis = funcToRun.runAtMillis; 1646 funcToRun.funcToCall(); 1647 if (funcToRun.recurring) { 1648 this.scheduleFunction(funcToRun.timeoutKey, 1649 funcToRun.funcToCall, 1650 funcToRun.millis, 1651 true); 1652 } 1653 } catch(e) { 1654 } 1655 } 1656 this.runFunctionsWithinRange(oldMillis, nowMillis); 1657 } 1658 }; 1659 1660 jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) { 1661 this.scheduledFunctions[timeoutKey] = { 1662 runAtMillis: this.nowMillis + millis, 1663 funcToCall: funcToCall, 1664 recurring: recurring, 1665 timeoutKey: timeoutKey, 1666 millis: millis 1667 }; 1668 }; 1669 1670 /** 1671 * @namespace 1672 */ 1673 jasmine.Clock = { 1674 defaultFakeTimer: new jasmine.FakeTimer(), 1675 1676 reset: function() { 1677 jasmine.Clock.assertInstalled(); 1678 jasmine.Clock.defaultFakeTimer.reset(); 1679 }, 1680 1681 tick: function(millis) { 1682 jasmine.Clock.assertInstalled(); 1683 jasmine.Clock.defaultFakeTimer.tick(millis); 1684 }, 1685 1686 runFunctionsWithinRange: function(oldMillis, nowMillis) { 1687 jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis); 1688 }, 1689 1690 scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) { 1691 jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring); 1692 }, 1693 1694 useMock: function() { 1695 if (!jasmine.Clock.isInstalled()) { 1696 var spec = jasmine.getEnv().currentSpec; 1697 spec.after(jasmine.Clock.uninstallMock); 1698 1699 jasmine.Clock.installMock(); 1700 } 1701 }, 1702 1703 installMock: function() { 1704 jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer; 1705 }, 1706 1707 uninstallMock: function() { 1708 jasmine.Clock.assertInstalled(); 1709 jasmine.Clock.installed = jasmine.Clock.real; 1710 }, 1711 1712 real: { 1713 setTimeout: jasmine.getGlobal().setTimeout, 1714 clearTimeout: jasmine.getGlobal().clearTimeout, 1715 setInterval: jasmine.getGlobal().setInterval, 1716 clearInterval: jasmine.getGlobal().clearInterval 1717 }, 1718 1719 assertInstalled: function() { 1720 if (!jasmine.Clock.isInstalled()) { 1721 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()"); 1722 } 1723 }, 1724 1725 isInstalled: function() { 1726 return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer; 1727 }, 1728 1729 installed: null 1730 }; 1731 jasmine.Clock.installed = jasmine.Clock.real; 1732 1733 //else for IE support 1734 jasmine.getGlobal().setTimeout = function(funcToCall, millis) { 1735 if (jasmine.Clock.installed.setTimeout.apply) { 1736 return jasmine.Clock.installed.setTimeout.apply(this, arguments); 1737 } else { 1738 return jasmine.Clock.installed.setTimeout(funcToCall, millis); 1739 } 1740 }; 1741 1742 jasmine.getGlobal().setInterval = function(funcToCall, millis) { 1743 if (jasmine.Clock.installed.setInterval.apply) { 1744 return jasmine.Clock.installed.setInterval.apply(this, arguments); 1745 } else { 1746 return jasmine.Clock.installed.setInterval(funcToCall, millis); 1747 } 1748 }; 1749 1750 jasmine.getGlobal().clearTimeout = function(timeoutKey) { 1751 if (jasmine.Clock.installed.clearTimeout.apply) { 1752 return jasmine.Clock.installed.clearTimeout.apply(this, arguments); 1753 } else { 1754 return jasmine.Clock.installed.clearTimeout(timeoutKey); 1755 } 1756 }; 1757 1758 jasmine.getGlobal().clearInterval = function(timeoutKey) { 1759 if (jasmine.Clock.installed.clearTimeout.apply) { 1760 return jasmine.Clock.installed.clearInterval.apply(this, arguments); 1761 } else { 1762 return jasmine.Clock.installed.clearInterval(timeoutKey); 1763 } 1764 }; 1765 1766 /** 1767 * @constructor 1768 */ 1769 jasmine.MultiReporter = function() { 1770 this.subReporters_ = []; 1771 }; 1772 jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter); 1773 1774 jasmine.MultiReporter.prototype.addReporter = function(reporter) { 1775 this.subReporters_.push(reporter); 1776 }; 1777 1778 (function() { 1779 var functionNames = [ 1780 "reportRunnerStarting", 1781 "reportRunnerResults", 1782 "reportSuiteResults", 1783 "reportSpecStarting", 1784 "reportSpecResults", 1785 "log" 1786 ]; 1787 for (var i = 0; i < functionNames.length; i++) { 1788 var functionName = functionNames[i]; 1789 jasmine.MultiReporter.prototype[functionName] = (function(functionName) { 1790 return function() { 1791 for (var j = 0; j < this.subReporters_.length; j++) { 1792 var subReporter = this.subReporters_[j]; 1793 if (subReporter[functionName]) { 1794 subReporter[functionName].apply(subReporter, arguments); 1795 } 1796 } 1797 }; 1798 })(functionName); 1799 } 1800 })(); 1801 /** 1802 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults 1803 * 1804 * @constructor 1805 */ 1806 jasmine.NestedResults = function() { 1807 /** 1808 * The total count of results 1809 */ 1810 this.totalCount = 0; 1811 /** 1812 * Number of passed results 1813 */ 1814 this.passedCount = 0; 1815 /** 1816 * Number of failed results 1817 */ 1818 this.failedCount = 0; 1819 /** 1820 * Was this suite/spec skipped? 1821 */ 1822 this.skipped = false; 1823 /** 1824 * @ignore 1825 */ 1826 this.items_ = []; 1827 }; 1828 1829 /** 1830 * Roll up the result counts. 1831 * 1832 * @param result 1833 */ 1834 jasmine.NestedResults.prototype.rollupCounts = function(result) { 1835 this.totalCount += result.totalCount; 1836 this.passedCount += result.passedCount; 1837 this.failedCount += result.failedCount; 1838 }; 1839 1840 /** 1841 * Adds a log message. 1842 * @param values Array of message parts which will be concatenated later. 1843 */ 1844 jasmine.NestedResults.prototype.log = function(values) { 1845 this.items_.push(new jasmine.MessageResult(values)); 1846 }; 1847 1848 /** 1849 * Getter for the results: message & results. 1850 */ 1851 jasmine.NestedResults.prototype.getItems = function() { 1852 return this.items_; 1853 }; 1854 1855 /** 1856 * Adds a result, tracking counts (total, passed, & failed) 1857 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result 1858 */ 1859 jasmine.NestedResults.prototype.addResult = function(result) { 1860 if (result.type != 'log') { 1861 if (result.items_) { 1862 this.rollupCounts(result); 1863 } else { 1864 this.totalCount++; 1865 if (result.passed()) { 1866 this.passedCount++; 1867 } else { 1868 this.failedCount++; 1869 } 1870 } 1871 } 1872 this.items_.push(result); 1873 }; 1874 1875 /** 1876 * @returns {Boolean} True if <b>everything</b> below passed 1877 */ 1878 jasmine.NestedResults.prototype.passed = function() { 1879 return this.passedCount === this.totalCount; 1880 }; 1881 /** 1882 * Base class for pretty printing for expectation results. 1883 */ 1884 jasmine.PrettyPrinter = function() { 1885 this.ppNestLevel_ = 0; 1886 }; 1887 1888 /** 1889 * Formats a value in a nice, human-readable string. 1890 * 1891 * @param value 1892 */ 1893 jasmine.PrettyPrinter.prototype.format = function(value) { 1894 this.ppNestLevel_++; 1895 try { 1896 if (value === jasmine.undefined) { 1897 this.emitScalar('undefined'); 1898 } else if (value === null) { 1899 this.emitScalar('null'); 1900 } else if (value === jasmine.getGlobal()) { 1901 this.emitScalar('<global>'); 1902 } else if (value.jasmineToString) { 1903 this.emitScalar(value.jasmineToString()); 1904 } else if (typeof value === 'string') { 1905 this.emitString(value); 1906 } else if (jasmine.isSpy(value)) { 1907 this.emitScalar("spy on " + value.identity); 1908 } else if (value instanceof RegExp) { 1909 this.emitScalar(value.toString()); 1910 } else if (typeof value === 'function') { 1911 this.emitScalar('Function'); 1912 } else if (typeof value.nodeType === 'number') { 1913 this.emitScalar('HTMLNode'); 1914 } else if (value instanceof Date) { 1915 this.emitScalar('Date(' + value + ')'); 1916 } else if (value.__Jasmine_been_here_before__) { 1917 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>'); 1918 } else if (jasmine.isArray_(value) || typeof value == 'object') { 1919 value.__Jasmine_been_here_before__ = true; 1920 if (jasmine.isArray_(value)) { 1921 this.emitArray(value); 1922 } else { 1923 this.emitObject(value); 1924 } 1925 delete value.__Jasmine_been_here_before__; 1926 } else { 1927 this.emitScalar(value.toString()); 1928 } 1929 } finally { 1930 this.ppNestLevel_--; 1931 } 1932 }; 1933 1934 jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) { 1935 for (var property in obj) { 1936 if (!obj.hasOwnProperty(property)) continue; 1937 if (property == '__Jasmine_been_here_before__') continue; 1938 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && 1939 obj.__lookupGetter__(property) !== null) : false); 1940 } 1941 }; 1942 1943 jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_; 1944 jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_; 1945 jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_; 1946 jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_; 1947 1948 jasmine.StringPrettyPrinter = function() { 1949 jasmine.PrettyPrinter.call(this); 1950 1951 this.string = ''; 1952 }; 1953 jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter); 1954 1955 jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) { 1956 this.append(value); 1957 }; 1958 1959 jasmine.StringPrettyPrinter.prototype.emitString = function(value) { 1960 this.append("'" + value + "'"); 1961 }; 1962 1963 jasmine.StringPrettyPrinter.prototype.emitArray = function(array) { 1964 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 1965 this.append("Array"); 1966 return; 1967 } 1968 1969 this.append('[ '); 1970 for (var i = 0; i < array.length; i++) { 1971 if (i > 0) { 1972 this.append(', '); 1973 } 1974 this.format(array[i]); 1975 } 1976 this.append(' ]'); 1977 }; 1978 1979 jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) { 1980 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) { 1981 this.append("Object"); 1982 return; 1983 } 1984 1985 var self = this; 1986 this.append('{ '); 1987 var first = true; 1988 1989 this.iterateObject(obj, function(property, isGetter) { 1990 if (first) { 1991 first = false; 1992 } else { 1993 self.append(', '); 1994 } 1995 1996 self.append(property); 1997 self.append(' : '); 1998 if (isGetter) { 1999 self.append('<getter>'); 2000 } else { 2001 self.format(obj[property]); 2002 } 2003 }); 2004 2005 this.append(' }'); 2006 }; 2007 2008 jasmine.StringPrettyPrinter.prototype.append = function(value) { 2009 this.string += value; 2010 }; 2011 jasmine.Queue = function(env) { 2012 this.env = env; 2013 2014 // parallel to blocks. each true value in this array means the block will 2015 // get executed even if we abort 2016 this.ensured = []; 2017 this.blocks = []; 2018 this.running = false; 2019 this.index = 0; 2020 this.offset = 0; 2021 this.abort = false; 2022 }; 2023 2024 jasmine.Queue.prototype.clone = function() { 2025 var queue = new jasmine.Queue(this.env); 2026 queue.ensured = this.ensured.slice(0) 2027 queue.blocks = this.blocks.slice(0) 2028 queue.running = this.running; 2029 queue.index = this.index; 2030 queue.offset = this.offset; 2031 queue.abort = this.abort; 2032 return queue; 2033 } 2034 2035 jasmine.Queue.prototype.addBefore = function(block, ensure) { 2036 if (ensure === jasmine.undefined) { 2037 ensure = false; 2038 } 2039 2040 this.blocks.unshift(block); 2041 this.ensured.unshift(ensure); 2042 }; 2043 2044 jasmine.Queue.prototype.add = function(block, ensure) { 2045 if (ensure === jasmine.undefined) { 2046 ensure = false; 2047 } 2048 2049 this.blocks.push(block); 2050 this.ensured.push(ensure); 2051 }; 2052 2053 jasmine.Queue.prototype.insertNext = function(block, ensure) { 2054 if (ensure === jasmine.undefined) { 2055 ensure = false; 2056 } 2057 2058 this.ensured.splice((this.index + this.offset + 1), 0, ensure); 2059 this.blocks.splice((this.index + this.offset + 1), 0, block); 2060 this.offset++; 2061 }; 2062 2063 jasmine.Queue.prototype.start = function(onComplete) { 2064 this.running = true; 2065 this.onComplete = onComplete; 2066 this.next_(); 2067 }; 2068 2069 jasmine.Queue.prototype.isRunning = function() { 2070 return this.running; 2071 }; 2072 2073 jasmine.Queue.LOOP_DONT_RECURSE = true; 2074 2075 jasmine.Queue.prototype.next_ = function() { 2076 var self = this; 2077 var goAgain = true; 2078 2079 while (goAgain) { 2080 goAgain = false; 2081 2082 if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) { 2083 var calledSynchronously = true; 2084 var completedSynchronously = false; 2085 2086 var onComplete = function () { 2087 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) { 2088 completedSynchronously = true; 2089 return; 2090 } 2091 2092 if (self.blocks[self.index] && self.blocks[self.index].abort) { 2093 self.abort = true; 2094 } 2095 2096 self.offset = 0; 2097 self.index++; 2098 2099 var now = new Date().getTime(); 2100 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) { 2101 self.env.lastUpdate = now; 2102 self.env.setTimeout(function() { 2103 self.next_(); 2104 }, 0); 2105 } else { 2106 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) { 2107 goAgain = true; 2108 } else { 2109 self.next_(); 2110 } 2111 } 2112 }; 2113 self.blocks[self.index].execute(onComplete); 2114 2115 calledSynchronously = false; 2116 if (completedSynchronously) { 2117 onComplete(); 2118 } 2119 2120 } else { 2121 self.running = false; 2122 if (self.onComplete) { 2123 self.onComplete(); 2124 } 2125 } 2126 } 2127 }; 2128 2129 jasmine.Queue.prototype.results = function() { 2130 var results = new jasmine.NestedResults(); 2131 for (var i = 0; i < this.blocks.length; i++) { 2132 if (this.blocks[i].results) { 2133 results.addResult(this.blocks[i].results()); 2134 } 2135 } 2136 return results; 2137 }; 2138 2139 2140 /** 2141 * Runner 2142 * 2143 * @constructor 2144 * @param {jasmine.Env} env 2145 */ 2146 jasmine.Runner = function(env) { 2147 var self = this; 2148 self.env = env; 2149 self.queue = new jasmine.Queue(env); 2150 self.before_ = []; 2151 self.after_ = []; 2152 self.suites_ = []; 2153 }; 2154 2155 jasmine.Runner.prototype.execute = function() { 2156 var self = this; 2157 if (self.env.reporter.reportRunnerStarting) { 2158 self.env.reporter.reportRunnerStarting(this); 2159 } 2160 self.queue.start(function () { 2161 self.finishCallback(); 2162 }); 2163 }; 2164 2165 jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) { 2166 beforeEachFunction.typeName = 'beforeEach'; 2167 this.before_.splice(0,0,beforeEachFunction); 2168 }; 2169 2170 jasmine.Runner.prototype.afterEach = function(afterEachFunction) { 2171 afterEachFunction.typeName = 'afterEach'; 2172 this.after_.splice(0,0,afterEachFunction); 2173 }; 2174 2175 2176 jasmine.Runner.prototype.finishCallback = function() { 2177 this.env.reporter.reportRunnerResults(this); 2178 }; 2179 2180 jasmine.Runner.prototype.addSuite = function(suite) { 2181 this.suites_.push(suite); 2182 }; 2183 2184 jasmine.Runner.prototype.add = function(block) { 2185 if (block instanceof jasmine.Suite) { 2186 this.addSuite(block); 2187 } 2188 this.queue.add(block); 2189 }; 2190 2191 jasmine.Runner.prototype.specs = function () { 2192 var suites = this.suites(); 2193 var specs = []; 2194 for (var i = 0; i < suites.length; i++) { 2195 specs = specs.concat(suites[i].specs()); 2196 } 2197 return specs; 2198 }; 2199 2200 jasmine.Runner.prototype.suites = function() { 2201 return this.suites_; 2202 }; 2203 2204 jasmine.Runner.prototype.topLevelSuites = function() { 2205 var topLevelSuites = []; 2206 for (var i = 0; i < this.suites_.length; i++) { 2207 if (!this.suites_[i].parentSuite) { 2208 topLevelSuites.push(this.suites_[i]); 2209 } 2210 } 2211 return topLevelSuites; 2212 }; 2213 2214 jasmine.Runner.prototype.results = function() { 2215 return this.queue.results(); 2216 }; 2217 /** 2218 * Internal representation of a Jasmine specification, or test. 2219 * 2220 * @constructor 2221 * @param {jasmine.Env} env 2222 * @param {jasmine.Suite} suite 2223 * @param {String} description 2224 */ 2225 jasmine.Spec = function(env, suite, description) { 2226 if (!env) { 2227 throw new Error('jasmine.Env() required'); 2228 } 2229 if (!suite) { 2230 throw new Error('jasmine.Suite() required'); 2231 } 2232 var spec = this; 2233 spec.id = env.nextSpecId ? env.nextSpecId() : null; 2234 spec.env = env; 2235 spec.suite = suite; 2236 spec.description = description; 2237 spec.queue = new jasmine.Queue(env); 2238 2239 spec.afterCallbacks = []; 2240 spec.spies_ = []; 2241 2242 spec.results_ = new jasmine.NestedResults(); 2243 spec.results_.description = description; 2244 spec.matchersClass = null; 2245 }; 2246 2247 jasmine.Spec.prototype.getFullName = function() { 2248 return this.suite.getFullName() + ' ' + this.description + '.'; 2249 }; 2250 2251 2252 jasmine.Spec.prototype.results = function() { 2253 return this.results_; 2254 }; 2255 2256 /** 2257 * All parameters are pretty-printed and concatenated together, then written to the spec's output. 2258 * 2259 * Be careful not to leave calls to <code>jasmine.log</code> in production code. 2260 */ 2261 jasmine.Spec.prototype.log = function() { 2262 return this.results_.log(arguments); 2263 }; 2264 2265 jasmine.Spec.prototype.runs = function (func) { 2266 var block = new jasmine.Block(this.env, func, this); 2267 this.addToQueue(block); 2268 return this; 2269 }; 2270 2271 jasmine.Spec.prototype.addToQueue = function (block) { 2272 if (this.queue.isRunning()) { 2273 this.queue.insertNext(block); 2274 } else { 2275 this.queue.add(block); 2276 } 2277 }; 2278 2279 /** 2280 * @param {jasmine.ExpectationResult} result 2281 */ 2282 jasmine.Spec.prototype.addMatcherResult = function(result) { 2283 if (this.retries > 0 && !result.passed_) { 2284 this.retry(); 2285 } else { 2286 this.results_.addResult(result); 2287 } 2288 }; 2289 2290 jasmine.Spec.prototype.expect = function(actual) { 2291 var positive = new (this.getMatchersClass_())(this.env, actual, this); 2292 positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); 2293 return positive; 2294 }; 2295 2296 /** 2297 * Waits a fixed time period before moving to the next block. 2298 * 2299 * @deprecated Use waitsFor() instead 2300 * @param {Number} timeout milliseconds to wait 2301 */ 2302 jasmine.Spec.prototype.waits = function(timeout) { 2303 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this); 2304 this.addToQueue(waitsFunc); 2305 return this; 2306 }; 2307 2308 /** 2309 * Waits for the latchFunction to return true before proceeding to the next block. 2310 * 2311 * @param {Function} latchFunction 2312 * @param {String} optional_timeoutMessage 2313 * @param {Number} optional_timeout 2314 */ 2315 jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) { 2316 var latchFunction_ = null; 2317 var optional_timeoutMessage_ = null; 2318 var optional_timeout_ = null; 2319 2320 for (var i = 0; i < arguments.length; i++) { 2321 var arg = arguments[i]; 2322 switch (typeof arg) { 2323 case 'function': 2324 latchFunction_ = arg; 2325 break; 2326 case 'string': 2327 optional_timeoutMessage_ = arg; 2328 break; 2329 case 'number': 2330 optional_timeout_ = arg; 2331 break; 2332 } 2333 } 2334 2335 if (optional_timeoutMessage_ == null) { 2336 const objectToCaptureStack = {} 2337 Error.captureStackTrace(objectToCaptureStack, waitsFor) 2338 const stack = objectToCaptureStack.stack 2339 const line = stack.split('\n')[1] 2340 optional_timeoutMessage_ = `condition ${line}` 2341 } 2342 2343 var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this); 2344 this.addToQueue(waitsForFunc); 2345 return this; 2346 }; 2347 2348 jasmine.Spec.prototype.fail = function (e, onComplete) { 2349 if (this.retries > 0) { 2350 this.retry() 2351 } else { 2352 var expectationResult = new jasmine.ExpectationResult({ 2353 passed: false, 2354 message: e ? jasmine.util.formatException(e) : 'Exception', 2355 trace: { stack: e.stack } 2356 }); 2357 this.results_.addResult(expectationResult); 2358 } 2359 }; 2360 2361 jasmine.Spec.prototype.getMatchersClass_ = function() { 2362 return this.matchersClass || this.env.matchersClass; 2363 }; 2364 2365 jasmine.Spec.prototype.addMatchers = function(matchersPrototype) { 2366 var parent = this.getMatchersClass_(); 2367 var newMatchersClass = function() { 2368 parent.apply(this, arguments); 2369 }; 2370 jasmine.util.inherit(newMatchersClass, parent); 2371 jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass); 2372 this.matchersClass = newMatchersClass; 2373 }; 2374 2375 jasmine.Spec.prototype.finishCallback = function() { 2376 this.env.reporter.reportSpecResults(this); 2377 }; 2378 2379 jasmine.Spec.prototype.finish = function(onComplete) { 2380 this.removeAllSpies(); 2381 this.finishCallback(); 2382 if (onComplete) { 2383 onComplete(); 2384 } 2385 }; 2386 2387 jasmine.Spec.prototype.after = function(doAfter) { 2388 if (this.queue.isRunning()) { 2389 this.queue.add(new jasmine.Block(this.env, doAfter, this), true); 2390 } else { 2391 this.afterCallbacks.unshift(doAfter); 2392 } 2393 }; 2394 2395 jasmine.Spec.prototype.execute = function(onComplete) { 2396 var spec = this; 2397 if (!spec.env.specFilter(spec)) { 2398 spec.results_.skipped = true; 2399 spec.finish(onComplete); 2400 return; 2401 } 2402 2403 this.env.reporter.reportSpecStarting(this); 2404 2405 spec.env.currentSpec = spec; 2406 2407 spec.addBeforesAndAftersToQueue(); 2408 2409 spec.queue.start(function () { 2410 spec.finish(onComplete); 2411 }); 2412 }; 2413 2414 jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() { 2415 var runner = this.env.currentRunner(); 2416 var i; 2417 2418 for (var suite = this.suite; suite; suite = suite.parentSuite) { 2419 for (i = 0; i < suite.before_.length; i++) { 2420 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this)); 2421 } 2422 } 2423 for (i = 0; i < runner.before_.length; i++) { 2424 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this)); 2425 } 2426 for (i = 0; i < this.afterCallbacks.length; i++) { 2427 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true); 2428 } 2429 for (suite = this.suite; suite; suite = suite.parentSuite) { 2430 for (i = 0; i < suite.after_.length; i++) { 2431 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true); 2432 } 2433 } 2434 for (i = 0; i < runner.after_.length; i++) { 2435 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true); 2436 } 2437 }; 2438 2439 jasmine.Spec.prototype.explodes = function() { 2440 throw 'explodes function should not have been called'; 2441 }; 2442 2443 jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) { 2444 if (obj == jasmine.undefined) { 2445 throw "spyOn could not find an object to spy upon for " + methodName + "()"; 2446 } 2447 2448 if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) { 2449 throw methodName + '() method does not exist'; 2450 } 2451 2452 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) { 2453 throw new Error(methodName + ' has already been spied upon'); 2454 } 2455 2456 var spyObj = jasmine.createSpy(methodName); 2457 2458 this.spies_.push(spyObj); 2459 spyObj.baseObj = obj; 2460 spyObj.methodName = methodName; 2461 spyObj.originalValue = obj[methodName]; 2462 2463 obj[methodName] = spyObj; 2464 2465 return spyObj; 2466 }; 2467 2468 jasmine.Spec.prototype.removeAllSpies = function() { 2469 for (var i = 0; i < this.spies_.length; i++) { 2470 var spy = this.spies_[i]; 2471 spy.baseObj[spy.methodName] = spy.originalValue; 2472 } 2473 this.spies_ = []; 2474 }; 2475 2476 jasmine.Spec.prototype.RETRY_FLAKY_TEST_AND_SLOW_DOWN_THE_BUILD = function() { 2477 if (this.retries == null) { 2478 this.retries = 5; 2479 this.originalQueue = this.queue.clone(); 2480 } 2481 } 2482 2483 jasmine.Spec.prototype.retry = function() { 2484 this.retries--; 2485 this.queue = this.originalQueue.clone(); 2486 this.suite.queue.insertNext(this) 2487 } 2488 2489 /** 2490 * Internal representation of a Jasmine suite. 2491 * 2492 * @constructor 2493 * @param {jasmine.Env} env 2494 * @param {String} description 2495 * @param {Function} specDefinitions 2496 * @param {jasmine.Suite} parentSuite 2497 */ 2498 jasmine.Suite = function(env, description, specDefinitions, parentSuite) { 2499 var self = this; 2500 self.id = env.nextSuiteId ? env.nextSuiteId() : null; 2501 self.description = description; 2502 self.queue = new jasmine.Queue(env); 2503 self.parentSuite = parentSuite; 2504 self.env = env; 2505 self.before_ = []; 2506 self.after_ = []; 2507 self.children_ = []; 2508 self.suites_ = []; 2509 self.specs_ = []; 2510 }; 2511 2512 jasmine.Suite.prototype.getFullName = function() { 2513 var fullName = this.description; 2514 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) { 2515 fullName = parentSuite.description + ' ' + fullName; 2516 } 2517 return fullName; 2518 }; 2519 2520 jasmine.Suite.prototype.finish = function(onComplete) { 2521 this.env.reporter.reportSuiteResults(this); 2522 this.finished = true; 2523 if (typeof(onComplete) == 'function') { 2524 onComplete(); 2525 } 2526 }; 2527 2528 jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) { 2529 beforeEachFunction.typeName = 'beforeEach'; 2530 this.before_.unshift(beforeEachFunction); 2531 }; 2532 2533 jasmine.Suite.prototype.afterEach = function(afterEachFunction) { 2534 afterEachFunction.typeName = 'afterEach'; 2535 this.after_.unshift(afterEachFunction); 2536 }; 2537 2538 jasmine.Suite.prototype.results = function() { 2539 return this.queue.results(); 2540 }; 2541 2542 jasmine.Suite.prototype.add = function(suiteOrSpec) { 2543 this.children_.push(suiteOrSpec); 2544 if (suiteOrSpec instanceof jasmine.Suite) { 2545 this.suites_.push(suiteOrSpec); 2546 this.env.currentRunner().addSuite(suiteOrSpec); 2547 } else { 2548 this.specs_.push(suiteOrSpec); 2549 } 2550 this.queue.add(suiteOrSpec); 2551 }; 2552 2553 jasmine.Suite.prototype.specs = function() { 2554 return this.specs_; 2555 }; 2556 2557 jasmine.Suite.prototype.suites = function() { 2558 return this.suites_; 2559 }; 2560 2561 jasmine.Suite.prototype.children = function() { 2562 return this.children_; 2563 }; 2564 2565 jasmine.Suite.prototype.execute = function(onComplete) { 2566 var self = this; 2567 this.queue.start(function () { 2568 self.finish(onComplete); 2569 }); 2570 }; 2571 jasmine.WaitsBlock = function(env, timeout, spec) { 2572 this.timeout = timeout; 2573 jasmine.Block.call(this, env, null, spec); 2574 }; 2575 2576 jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block); 2577 2578 jasmine.WaitsBlock.prototype.execute = function (onComplete) { 2579 if (jasmine.VERBOSE) { 2580 this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...'); 2581 } 2582 this.env.setTimeout(function () { 2583 onComplete(); 2584 }, this.timeout); 2585 }; 2586 /** 2587 * A block which waits for some condition to become true, with timeout. 2588 * 2589 * @constructor 2590 * @extends jasmine.Block 2591 * @param {jasmine.Env} env The Jasmine environment. 2592 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true. 2593 * @param {Function} latchFunction A function which returns true when the desired condition has been met. 2594 * @param {String} message The message to display if the desired condition hasn't been met within the given time period. 2595 * @param {jasmine.Spec} spec The Jasmine spec. 2596 */ 2597 jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) { 2598 this.timeout = timeout || env.defaultTimeoutInterval; 2599 this.latchFunction = latchFunction; 2600 this.message = message; 2601 this.totalTimeSpentWaitingForLatch = 0; 2602 jasmine.Block.call(this, env, null, spec); 2603 }; 2604 jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block); 2605 2606 jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10; 2607 2608 jasmine.WaitsForBlock.prototype.execute = function(onComplete) { 2609 if (jasmine.VERBOSE) { 2610 this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen')); 2611 } 2612 2613 if (this.latchFunction.length > 0) { this.waitForExplicitCompletion(onComplete); return; } 2614 2615 var latchFunctionResult; 2616 try { 2617 latchFunctionResult = this.latchFunction.apply(this.spec); 2618 } catch (e) { 2619 this.spec.fail(e); 2620 onComplete(); 2621 return; 2622 } 2623 2624 if (latchFunctionResult) { 2625 onComplete(); 2626 } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) { 2627 var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen'); 2628 this.spec.fail({ 2629 name: 'timeout', 2630 message: message 2631 }); 2632 2633 this.abort = true; 2634 onComplete(); 2635 } else { 2636 this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT; 2637 var self = this; 2638 this.env.setTimeout(function() { 2639 self.execute(onComplete); 2640 }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT); 2641 } 2642 }; 2643 2644 jasmine.WaitsForBlock.prototype.waitForExplicitCompletion = function(onComplete) { 2645 var self = this; 2646 var timeoutHandle = this.env.setTimeout(function() { 2647 var message = 'timed out after ' + self.timeout + ' msec waiting for ' + (self.message || 'something to happen'); 2648 self.spec.fail({ 2649 name: 'timeout', 2650 message: message 2651 }); 2652 multiCompletion.cancelled = true; 2653 self.abort = true; 2654 onComplete(); 2655 }, this.timeout); 2656 2657 var multiCompletion = new jasmine.WaitsForBlock.MultiCompletion(this.latchFunction.length, this.env, onComplete, timeoutHandle); 2658 2659 try { 2660 this.latchFunction.apply(this.spec, multiCompletion.completionFunctions); 2661 } catch (e) { 2662 this.spec.fail(e); 2663 onComplete(); 2664 return; 2665 } 2666 }; 2667 2668 jasmine.WaitsForBlock.MultiCompletion = function(count, env, onComplete, timeoutHandle) { 2669 this.count = count; 2670 this.env = env; 2671 this.onComplete = onComplete; 2672 this.timeoutHandle = timeoutHandle; 2673 this.completionStatuses = []; 2674 this.completionFunctions = []; 2675 2676 for (var i = 0; i < count; i++) { 2677 this.completionStatuses.push(false); 2678 this.completionFunctions.push(this.buildCompletionFunction(i)); 2679 } 2680 }; 2681 2682 jasmine.WaitsForBlock.MultiCompletion.prototype.attemptCompletion = function() { 2683 if (this.cancelled) return; 2684 for (var j = 0; j < this.count; j++) { 2685 if (!this.completionStatuses[j]) return; 2686 } 2687 this.env.clearTimeout(this.timeoutHandle); 2688 this.onComplete(); 2689 }; 2690 2691 jasmine.WaitsForBlock.MultiCompletion.prototype.buildCompletionFunction = function(i) { 2692 var self = this; 2693 var spent = false; 2694 return function() { 2695 if (spent) return; 2696 spent = true; 2697 self.completionStatuses[i] = true; 2698 self.attemptCompletion(); 2699 }; 2700 }; 2701 2702 jasmine.version_= { 2703 "major": 1, 2704 "minor": 3, 2705 "build": 1, 2706 "revision": 1354556913 2707 };