1 var isCommonJS
= typeof window
== "undefined" && typeof exports
== "object";
4 * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.
9 if (isCommonJS
) exports
.jasmine
= jasmine
;
13 jasmine
.unimplementedMethod_ = function() {
14 throw new Error("unimplemented method");
18 * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just
19 * a plain old variable and may be redefined by somebody else.
23 jasmine
.undefined = jasmine
.___undefined___
;
26 * Show diagnostic messages in the console if set to true
29 jasmine
.VERBOSE
= false;
32 * 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.
35 jasmine
.DEFAULT_UPDATE_INTERVAL
= 250;
38 * Maximum levels of nesting that will be included when an object is pretty-printed
40 jasmine
.MAX_PRETTY_PRINT_DEPTH
= 40;
43 * Default timeout interval in milliseconds for waitsFor() blocks.
45 jasmine
.DEFAULT_TIMEOUT_INTERVAL
= 5000;
48 * 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.
49 * Set to false to let the exception bubble up in the browser.
52 jasmine
.CATCH_EXCEPTIONS
= true;
54 jasmine
.getGlobal = function() {
55 function getGlobal() {
63 * Allows for bound functions to be compared. Internal use only.
67 * @param base {Object} bound 'this' for the function
68 * @param name {Function} function to find
70 jasmine
.bindOriginal_ = function(base
, name
) {
71 var original
= base
[name
];
74 return original
.apply(base
, arguments
);
78 return jasmine
.getGlobal()[name
];
82 jasmine
.setTimeout
= jasmine
.bindOriginal_(jasmine
.getGlobal(), 'setTimeout');
83 jasmine
.clearTimeout
= jasmine
.bindOriginal_(jasmine
.getGlobal(), 'clearTimeout');
84 jasmine
.setInterval
= jasmine
.bindOriginal_(jasmine
.getGlobal(), 'setInterval');
85 jasmine
.clearInterval
= jasmine
.bindOriginal_(jasmine
.getGlobal(), 'clearInterval');
87 jasmine
.MessageResult = function(values
) {
90 this.trace
= new Error(); // todo: test better
93 jasmine
.MessageResult
.prototype.toString = function() {
95 for (var i
= 0; i
< this.values
.length
; i
++) {
96 if (i
> 0) text
+= " ";
97 if (jasmine
.isString_(this.values
[i
])) {
98 text
+= this.values
[i
];
100 text
+= jasmine
.pp(this.values
[i
]);
106 jasmine
.ExpectationResult = function(params
) {
107 this.type
= 'expect';
108 this.matcherName
= params
.matcherName
;
109 this.passed_
= params
.passed
;
110 this.expected
= params
.expected
;
111 this.actual
= params
.actual
;
112 this.message
= this.passed_
? 'Passed.' : params
.message
;
114 var trace
= (params
.trace
|| new Error(this.message
));
115 this.trace
= this.passed_
? '' : trace
+ '';
118 jasmine
.ExpectationResult
.prototype.toString = function () {
122 jasmine
.ExpectationResult
.prototype.passed = function () {
127 * Getter for the Jasmine environment. Ensures one gets created
129 jasmine
.getEnv = function() {
130 var env
= jasmine
.currentEnv_
= jasmine
.currentEnv_
|| new jasmine
.Env();
140 jasmine
.isArray_ = function(value
) {
141 return jasmine
.isA_("Array", value
);
150 jasmine
.isString_ = function(value
) {
151 return jasmine
.isA_("String", value
);
160 jasmine
.isNumber_ = function(value
) {
161 return jasmine
.isA_("Number", value
);
167 * @param {String} typeName
171 jasmine
.isA_ = function(typeName
, value
) {
172 return Object
.prototype.toString
.apply(value
) === '[object ' + typeName
+ ']';
176 * Pretty printer for expecations. Takes any object and turns it into a human-readable string.
178 * @param value {Object} an object to be outputted
181 jasmine
.pp = function(value
) {
182 var stringPrettyPrinter
= new jasmine
.StringPrettyPrinter();
183 stringPrettyPrinter
.format(value
);
184 return stringPrettyPrinter
.string
;
188 * Returns true if the object is a DOM Node.
190 * @param {Object} obj object to check
193 jasmine
.isDomNode = function(obj
) {
194 return obj
.nodeType
> 0;
198 * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.
201 * // don't care about which function is passed in, as long as it's a function
202 * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));
204 * @param {Class} clazz
205 * @returns matchable object of the type clazz
207 jasmine
.any = function(clazz
) {
208 return new jasmine
.Matchers
.Any(clazz
);
212 * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the
213 * attributes on the object.
216 * // don't care about any other attributes than foo.
217 * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});
219 * @param sample {Object} sample
220 * @returns matchable object for the sample
222 jasmine
.objectContaining = function (sample
) {
223 return new jasmine
.Matchers
.ObjectContaining(sample
);
227 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.
229 * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine
230 * expectation syntax. Spies can be checked if they were called or not and what the calling params were.
232 * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).
234 * Spies are torn down at the end of every spec.
236 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.
240 * var myStub = jasmine.createSpy('myStub'); // can be used anywhere
244 * not: function(bool) { return !bool; }
247 * // actual foo.not will not be called, execution stops
250 // foo.not spied upon, execution will continue to implementation
251 * spyOn(foo, 'not').andCallThrough();
255 * not: function(bool) { return !bool; }
258 * // foo.not(val) will return val
259 * spyOn(foo, 'not').andCallFake(function(value) {return value;});
263 * expect(foo.not).toHaveBeenCalled();
264 * expect(foo.not).toHaveBeenCalledWith(true);
267 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj
268 * @param {String} name
270 jasmine
.Spy = function(name
) {
272 * The name of the spy, if provided.
274 this.identity
= name
|| 'unknown';
276 * Is this Object a spy?
280 * The actual function this spy stubs.
282 this.plan = function() {
285 * Tracking of the most recent call to the spy.
287 * var mySpy = jasmine.createSpy('foo');
289 * mySpy.mostRecentCall.args = [1, 2];
291 this.mostRecentCall
= {};
294 * Holds arguments for each call to the spy, indexed by call count
296 * var mySpy = jasmine.createSpy('foo');
299 * mySpy.mostRecentCall.args = [7, 8];
300 * mySpy.argsForCall[0] = [1, 2];
301 * mySpy.argsForCall[1] = [7, 8];
303 this.argsForCall
= [];
308 * Tells a spy to call through to the actual implemenatation.
312 * bar: function() { // do some stuff }
315 * // defining a spy on an existing property: foo.bar
316 * spyOn(foo, 'bar').andCallThrough();
318 jasmine
.Spy
.prototype.andCallThrough = function() {
319 this.plan
= this.originalValue
;
324 * For setting the return value of a spy.
327 * // defining a spy from scratch: foo() returns 'baz'
328 * var foo = jasmine.createSpy('spy on foo').andReturn('baz');
330 * // defining a spy on an existing property: foo.bar() returns 'baz'
331 * spyOn(foo, 'bar').andReturn('baz');
333 * @param {Object} value
335 jasmine
.Spy
.prototype.andReturn = function(value
) {
336 this.plan = function() {
343 * For throwing an exception when a spy is called.
346 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'
347 * var foo = jasmine.createSpy('spy on foo').andThrow('baz');
349 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'
350 * spyOn(foo, 'bar').andThrow('baz');
352 * @param {String} exceptionMsg
354 jasmine
.Spy
.prototype.andThrow = function(exceptionMsg
) {
355 this.plan = function() {
362 * Calls an alternate implementation when a spy is called.
365 * var baz = function() {
366 * // do some stuff, return something
368 * // defining a spy from scratch: foo() calls the function baz
369 * var foo = jasmine.createSpy('spy on foo').andCall(baz);
371 * // defining a spy on an existing property: foo.bar() calls an anonymnous function
372 * spyOn(foo, 'bar').andCall(function() { return 'baz';} );
374 * @param {Function} fakeFunc
376 jasmine
.Spy
.prototype.andCallFake = function(fakeFunc
) {
377 this.plan
= fakeFunc
;
382 * Resets all of a spy's the tracking variables so that it can be used again.
389 * expect(foo.bar.callCount).toEqual(1);
393 * expect(foo.bar.callCount).toEqual(0);
395 jasmine
.Spy
.prototype.reset = function() {
396 this.wasCalled
= false;
398 this.argsForCall
= [];
400 this.mostRecentCall
= {};
403 jasmine
.createSpy = function(name
) {
405 var spyObj = function() {
406 spyObj
.wasCalled
= true;
408 var args
= jasmine
.util
.argsToArray(arguments
);
409 spyObj
.mostRecentCall
.object
= this;
410 spyObj
.mostRecentCall
.args
= args
;
411 spyObj
.argsForCall
.push(args
);
412 spyObj
.calls
.push({object
: this, args
: args
});
413 return spyObj
.plan
.apply(this, arguments
);
416 var spy
= new jasmine
.Spy(name
);
418 for (var prop
in spy
) {
419 spyObj
[prop
] = spy
[prop
];
428 * Determines whether an object is a spy.
430 * @param {jasmine.Spy|Object} putativeSpy
433 jasmine
.isSpy = function(putativeSpy
) {
434 return putativeSpy
&& putativeSpy
.isSpy
;
438 * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something
441 * @param {String} baseName name of spy class
442 * @param {Array} methodNames array of names of methods to make spies
444 jasmine
.createSpyObj = function(baseName
, methodNames
) {
445 if (!jasmine
.isArray_(methodNames
) || methodNames
.length
=== 0) {
446 throw new Error('createSpyObj requires a non-empty array of method names to create spies for');
449 for (var i
= 0; i
< methodNames
.length
; i
++) {
450 obj
[methodNames
[i
]] = jasmine
.createSpy(baseName
+ '.' + methodNames
[i
]);
456 * All parameters are pretty-printed and concatenated together, then written to the current spec's output.
458 * Be careful not to leave calls to <code>jasmine.log</code> in production code.
460 jasmine
.log = function() {
461 var spec
= jasmine
.getEnv().currentSpec
;
462 spec
.log
.apply(spec
, arguments
);
466 * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.
471 * not: function(bool) { return !bool; }
473 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops
475 * @see jasmine.createSpy
478 * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods
480 var spyOn = function(obj
, methodName
) {
481 return jasmine
.getEnv().currentSpec
.spyOn(obj
, methodName
);
483 if (isCommonJS
) exports
.spyOn
= spyOn
;
486 * Creates a Jasmine spec that will be added to the current suite.
488 * // TODO: pending tests
491 * it('should be true', function() {
492 * expect(true).toEqual(true);
495 * @param {String} desc description of this specification
496 * @param {Function} func defines the preconditions and expectations of the spec
498 var it = function(desc
, func
) {
499 return jasmine
.getEnv().it(desc
, func
);
501 if (isCommonJS
) exports
.it
= it
;
504 * Creates a <em>disabled</em> Jasmine spec.
506 * A convenience method that allows existing specs to be disabled temporarily during development.
508 * @param {String} desc description of this specification
509 * @param {Function} func defines the preconditions and expectations of the spec
511 var xit = function(desc
, func
) {
512 return jasmine
.getEnv().xit(desc
, func
);
514 if (isCommonJS
) exports
.xit
= xit
;
517 * Starts a chain for a Jasmine expectation.
519 * It is passed an Object that is the actual value and should chain to one of the many
520 * jasmine.Matchers functions.
522 * @param {Object} actual Actual value to test against and expected value
523 * @return {jasmine.Matchers}
525 var expect = function(actual
) {
526 return jasmine
.getEnv().currentSpec
.expect(actual
);
528 if (isCommonJS
) exports
.expect
= expect
;
531 * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.
533 * @param {Function} func Function that defines part of a jasmine spec.
535 var runs = function(func
) {
536 jasmine
.getEnv().currentSpec
.runs(func
);
538 if (isCommonJS
) exports
.runs
= runs
;
541 * Waits a fixed time period before moving to the next block.
543 * @deprecated Use waitsFor() instead
544 * @param {Number} timeout milliseconds to wait
546 var waits = function(timeout
) {
547 jasmine
.getEnv().currentSpec
.waits(timeout
);
549 if (isCommonJS
) exports
.waits
= waits
;
552 * Waits for the latchFunction to return true before proceeding to the next block.
554 * @param {Function} latchFunction
555 * @param {String} optional_timeoutMessage
556 * @param {Number} optional_timeout
557 * @param {Number} optional_timeout_increment
559 var waitsFor = function(latchFunction
, optional_timeoutMessage
, optional_timeout
, optional_timeout_increment
) {
560 jasmine
.getEnv().currentSpec
.waitsFor
.apply(jasmine
.getEnv().currentSpec
, arguments
);
562 if (isCommonJS
) exports
.waitsFor
= waitsFor
;
565 * A function that is called before each spec in a suite.
567 * Used for spec setup, including validating assumptions.
569 * @param {Function} beforeEachFunction
571 var beforeEach = function(beforeEachFunction
) {
572 jasmine
.getEnv().beforeEach(beforeEachFunction
);
574 if (isCommonJS
) exports
.beforeEach
= beforeEach
;
577 * A function that is called after each spec in a suite.
579 * Used for restoring any state that is hijacked during spec execution.
581 * @param {Function} afterEachFunction
583 var afterEach = function(afterEachFunction
) {
584 jasmine
.getEnv().afterEach(afterEachFunction
);
586 if (isCommonJS
) exports
.afterEach
= afterEach
;
589 * Defines a suite of specifications.
591 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared
592 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization
593 * of setup in some tests.
596 * // TODO: a simple suite
598 * // TODO: a simple suite with a nested describe block
600 * @param {String} description A string, usually the class under test.
601 * @param {Function} specDefinitions function that defines several specs.
603 var describe = function(description
, specDefinitions
) {
604 return jasmine
.getEnv().describe(description
, specDefinitions
);
606 if (isCommonJS
) exports
.describe
= describe
;
609 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.
611 * @param {String} description A string, usually the class under test.
612 * @param {Function} specDefinitions function that defines several specs.
614 var xdescribe = function(description
, specDefinitions
) {
615 return jasmine
.getEnv().xdescribe(description
, specDefinitions
);
617 if (isCommonJS
) exports
.xdescribe
= xdescribe
;
620 // Provide the XMLHttpRequest class for IE 5.x-6.x:
621 jasmine
.XmlHttpRequest
= (typeof XMLHttpRequest
== "undefined") ? function() {
630 var xhr
= tryIt(function() {
631 return new ActiveXObject("Msxml2.XMLHTTP.6.0");
634 return new ActiveXObject("Msxml2.XMLHTTP.3.0");
637 return new ActiveXObject("Msxml2.XMLHTTP");
640 return new ActiveXObject("Microsoft.XMLHTTP");
643 if (!xhr
) throw new Error("This browser does not support XMLHttpRequest.");
647 jasmine
.MAX_PRETTY_PRINT_DEPTH
= 1;
651 jasmine
.hashString = function (s
, hash
) {
654 // see http://www.cse.yorku.ca/~oz/hash.html
655 for (var c
, i
= 0, n
= s
.length
; i
< n
; ++i
) {
657 hash
= c
+ (hash
<< 6) + (hash
<< 16) - hash
;
660 if (jasmine
.hashes
[hash
]) {
661 jasmine
.getEnv().reporter
.log("Identical hash detected: " + s
);
664 jasmine
.hashes
[hash
] = true;
668 jasmine
.toMap = function (items
) {
672 for (k
= items
.length
; k
--; ) {
673 map
[items
[k
]] = true;
679 // Override jasmine to track the spy call results as well
680 // as their invocation arguments
681 jasmine
.createSpy = function(name
) {
683 var spyObj = function() {
684 spyObj
.wasCalled
= true;
687 var args
= jasmine
.util
.argsToArray(arguments
);
688 var result
= spyObj
.plan
.apply(this, arguments
);
690 spyObj
.mostRecentCall
.object
= spyObj
.mostRecentCall
.scope
= this;
691 spyObj
.mostRecentCall
.args
= args
;
692 spyObj
.mostRecentCall
.result
= result
;
694 spyObj
.argsForCall
.push(args
);
696 spyObj
.calls
.push({object
: this, scope
: this, args
: args
, result
: result
});
701 var spy
= new jasmine
.Spy(name
);
703 for (var prop
in spy
) {
704 spyObj
[prop
] = spy
[prop
];
712 jasmine
.setCurrentScript = function(file
){
713 if(typeof Ext
!== "undefined" && Ext
.cmd
&& Ext
.cmd
.api
&& Ext
.cmd
.api
.adapter
) {
714 Ext
.cmd
.api
.adapter
.setCurrentScript(file
);
718 jasmine
.getCurrentScript = function() {
719 if(typeof Ext
!== "undefined" && Ext
.cmd
&& Ext
.cmd
.api
&& Ext
.cmd
.api
.adapter
) {
720 return Ext
.cmd
.api
.adapter
.getCurrentScript();
725 jasmine
.setOptions = function(jsonString
) {
726 jasmine
._options
= JSON
.parse(jsonString
);
729 jasmine
.getOptions = function() {
730 return jasmine
._options
|| {};
733 jasmine
.initDebug = function() {
734 var spec
= jasmine
.getOptions().spec
;
736 var specId
= parseInt(spec
);
737 this.getEnv().specFilter = function(spec
) {
738 if (spec
.id
=== specId
) {
739 spec
.debugBlocks
= true;
748 jasmine
.generateDebuggableBlock = function(fn
) {
751 /* Step into the function below */
752 fn
.apply(this, arguments
);
756 jasmine
.showDebugPrompt = function(callback
) {
757 if (navigator
.userAgent
.toLowerCase().match(/chrome|safari|msie/)) {
758 var div
= document
.createElement("div");
760 div
.setAttribute("style",[
761 "background:#E5E4E2;",
763 "position:absolute;",
767 "border-radius: 5px;",
768 "border:1px solid #777;",
769 "text-align:center;",
770 "box-shadow: 5px 5px 5px #888;"
774 '<p>Open the developer tools to debug and press ok.</p>',
775 '<button id="sencha-debug-button">OK</button>',
779 document
.body
.appendChild(div
);
781 var button
= document
.getElementById("sencha-debug-button");
783 var onClick = function() {
784 if (button
.removeEventListener
) {
785 button
.removeEventListener("click", onClick
, false);
787 button
.detachEvent("onmousedown", onClick
);
789 document
.body
.removeChild(div
);
794 if (button
.addEventListener
) {
795 button
.addEventListener("click", onClick
, false);
797 button
.attachEvent("onmousedown", onClick
);
804 jasmine
.getByIds = function (items
, ids
) {
806 length
= items
.length
,
809 for (i
= 0; i
< length
; ++i
) {
820 var specFor = function(object
, specForFn
) {
821 jasmine
.getEnv().specFor(object
, specForFn
);
824 var xspecFor = function(object
, specForFn
) {
825 jasmine
.getEnv().xspecFor(object
, specForFn
);
828 var xdescribe = function(description
, specDefinitions
, coverageFile
) {
829 return jasmine
.getEnv().describe(description
, specDefinitions
, coverageFile
).disable();
832 var xit = function(desc
, func
) {
833 return jasmine
.getEnv().it(desc
, func
).disable();
841 * Declare that a child class inherit it's prototype from the parent class.
844 * @param {Function} childClass
845 * @param {Function} parentClass
847 jasmine
.util
.inherit = function(childClass
, parentClass
) {
851 var subclass = function() {
853 subclass
.prototype = parentClass
.prototype;
854 childClass
.prototype = new subclass();
857 jasmine
.util
.formatException = function(e
) {
862 else if (e
.lineNumber
) {
863 lineNumber
= e
.lineNumber
;
871 else if (e
.fileName
) {
875 var message
= (e
.name
&& e
.message
) ? (e
.name
+ ': ' + e
.message
) : e
.toString();
877 if (file
&& lineNumber
) {
878 message
+= ' in ' + file
+ ' (line ' + lineNumber
+ ')';
884 jasmine
.util
.htmlEscape = function(str
) {
885 if (!str
) return str
;
886 return str
.replace(/&/g
, '&')
887 .replace(/</g
, '<')
888 .replace(/>/g
, '>');
891 jasmine
.util
.argsToArray = function(args
) {
892 var arrayOfArgs
= [];
893 for (var i
= 0; i
< args
.length
; i
++) arrayOfArgs
.push(args
[i
]);
897 jasmine
.util
.extend = function(destination
, source
) {
898 for (var property
in source
) destination
[property
] = source
[property
];
901 jasmine
.util
.getOrigin = function() {
902 var port
= window
.location
.port
;
905 origin
= window
.location
.protocol
+ "//" + window
.location
.hostname
;
908 origin
+= ":" + port
;
914 jasmine
.util
.getFileFromContextMapping = function(file
) {
915 var contextMapping
= jasmine
.contextMapping
;
916 if (file
&& contextMapping
) {
917 var origin
= jasmine
.util
.getOrigin();
918 for (var context
in contextMapping
) {
919 file
= file
.replace(origin
+ context
, contextMapping
[context
]);
925 jasmine
.util
.formatException = function(e
) {
930 else if (e
.lineNumber
) {
931 lineNumber
= e
.lineNumber
;
939 else if (e
.fileName
) {
943 file
= jasmine
.util
.getFileFromContextMapping(file
);
945 var message
= (e
.name
&& e
.message
) ? (e
.name
+ ': ' + e
.message
) : e
.toString();
947 if (file
&& lineNumber
) {
948 message
+= ' in ' + file
+ ' (line ' + lineNumber
+ ')';
953 * Environment for Jasmine
957 jasmine
.Env = function() {
958 this.currentSpec
= null;
959 this.currentSuite
= null;
960 this.currentRunner_
= new jasmine
.Runner(this);
962 this.reporter
= new jasmine
.MultiReporter();
964 this.updateInterval
= jasmine
.DEFAULT_UPDATE_INTERVAL
;
965 this.defaultTimeoutInterval
= jasmine
.DEFAULT_TIMEOUT_INTERVAL
;
967 this.specFilter = function() {
971 this.nextSpecId_
= 0;
972 this.nextSuiteId_
= 0;
973 this.equalityTesters_
= [];
976 this.matchersClass = function() {
977 jasmine
.Matchers
.apply(this, arguments
);
979 jasmine
.util
.inherit(this.matchersClass
, jasmine
.Matchers
);
981 jasmine
.Matchers
.wrapInto_(jasmine
.Matchers
.prototype, this.matchersClass
);
985 jasmine
.Env
.prototype.setTimeout
= jasmine
.setTimeout
;
986 jasmine
.Env
.prototype.clearTimeout
= jasmine
.clearTimeout
;
987 jasmine
.Env
.prototype.setInterval
= jasmine
.setInterval
;
988 jasmine
.Env
.prototype.clearInterval
= jasmine
.clearInterval
;
991 * @returns an object containing jasmine version build info, if set.
993 jasmine
.Env
.prototype.version = function () {
994 if (jasmine
.version_
) {
995 return jasmine
.version_
;
997 throw new Error('Version not set');
1002 * @returns string containing jasmine version build info, if set.
1004 jasmine
.Env
.prototype.versionString = function() {
1005 if (!jasmine
.version_
) {
1006 return "version unknown";
1009 var version
= this.version();
1010 var versionString
= version
.major
+ "." + version
.minor
+ "." + version
.build
;
1011 if (version
.release_candidate
) {
1012 versionString
+= ".rc" + version
.release_candidate
;
1014 versionString
+= " revision " + version
.revision
;
1015 return versionString
;
1019 * @returns a sequential integer starting at 0
1021 jasmine
.Env
.prototype.nextSpecId = function () {
1022 return this.nextSpecId_
++;
1026 * @returns a sequential integer starting at 0
1028 jasmine
.Env
.prototype.nextSuiteId = function () {
1029 return this.nextSuiteId_
++;
1033 * Register a reporter to receive status updates from Jasmine.
1034 * @param {jasmine.Reporter} reporter An object which will receive status updates.
1036 jasmine
.Env
.prototype.addReporter = function(reporter
) {
1037 this.reporter
.addReporter(reporter
);
1040 jasmine
.Env
.prototype.execute = function() {
1041 this.currentRunner_
.execute();
1044 jasmine
.Env
.prototype.describe = function(description
, specDefinitions
) {
1045 var suite
= new jasmine
.Suite(this, description
, specDefinitions
, this.currentSuite
);
1047 var parentSuite
= this.currentSuite
;
1049 parentSuite
.add(suite
);
1051 this.currentRunner_
.add(suite
);
1054 this.currentSuite
= suite
;
1056 var declarationError
= null;
1058 specDefinitions
.call(suite
);
1060 declarationError
= e
;
1063 if (declarationError
) {
1064 this.it("encountered a declaration exception", function() {
1065 throw declarationError
;
1069 this.currentSuite
= parentSuite
;
1074 jasmine
.Env
.prototype.beforeEach = function(beforeEachFunction
) {
1075 if (this.currentSuite
) {
1076 this.currentSuite
.beforeEach(beforeEachFunction
);
1078 this.currentRunner_
.beforeEach(beforeEachFunction
);
1082 jasmine
.Env
.prototype.currentRunner = function () {
1083 return this.currentRunner_
;
1086 jasmine
.Env
.prototype.afterEach = function(afterEachFunction
) {
1087 if (this.currentSuite
) {
1088 this.currentSuite
.afterEach(afterEachFunction
);
1090 this.currentRunner_
.afterEach(afterEachFunction
);
1095 jasmine
.Env
.prototype.xdescribe = function(desc
, specDefinitions
) {
1097 execute: function() {
1102 jasmine
.Env
.prototype.it = function(description
, func
) {
1103 var spec
= new jasmine
.Spec(this, this.currentSuite
, description
);
1104 this.currentSuite
.add(spec
);
1105 this.currentSpec
= spec
;
1114 jasmine
.Env
.prototype.xit = function(desc
, func
) {
1116 id
: this.nextSpecId(),
1122 jasmine
.Env
.prototype.compareRegExps_ = function(a
, b
, mismatchKeys
, mismatchValues
) {
1123 if (a
.source
!= b
.source
)
1124 mismatchValues
.push("expected pattern /" + b
.source
+ "/ is not equal to the pattern /" + a
.source
+ "/");
1126 if (a
.ignoreCase
!= b
.ignoreCase
)
1127 mismatchValues
.push("expected modifier i was" + (b
.ignoreCase
? " " : " not ") + "set and does not equal the origin modifier");
1129 if (a
.global
!= b
.global
)
1130 mismatchValues
.push("expected modifier g was" + (b
.global
? " " : " not ") + "set and does not equal the origin modifier");
1132 if (a
.multiline
!= b
.multiline
)
1133 mismatchValues
.push("expected modifier m was" + (b
.multiline
? " " : " not ") + "set and does not equal the origin modifier");
1135 if (a
.sticky
!= b
.sticky
)
1136 mismatchValues
.push("expected modifier y was" + (b
.sticky
? " " : " not ") + "set and does not equal the origin modifier");
1138 return (mismatchValues
.length
=== 0);
1141 jasmine
.Env
.prototype.compareObjects_ = function(a
, b
, mismatchKeys
, mismatchValues
) {
1142 if (a
.__Jasmine_been_here_before__
=== b
&& b
.__Jasmine_been_here_before__
=== a
) {
1146 a
.__Jasmine_been_here_before__
= b
;
1147 b
.__Jasmine_been_here_before__
= a
;
1149 var hasKey = function(obj
, keyName
) {
1150 return obj
!== null && obj
[keyName
] !== jasmine
.undefined;
1153 for (var property
in b
) {
1154 if (!hasKey(a
, property
) && hasKey(b
, property
)) {
1155 mismatchKeys
.push("expected has key '" + property
+ "', but missing from actual.");
1158 for (property
in a
) {
1159 if (!hasKey(b
, property
) && hasKey(a
, property
)) {
1160 mismatchKeys
.push("expected missing key '" + property
+ "', but present in actual.");
1163 for (property
in b
) {
1164 if (property
== '__Jasmine_been_here_before__') continue;
1165 if (!this.equals_(a
[property
], b
[property
], mismatchKeys
, mismatchValues
)) {
1166 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.");
1170 if (jasmine
.isArray_(a
) && jasmine
.isArray_(b
) && a
.length
!= b
.length
) {
1171 mismatchValues
.push("arrays were not the same length");
1174 delete a
.__Jasmine_been_here_before__
;
1175 delete b
.__Jasmine_been_here_before__
;
1176 return (mismatchKeys
.length
=== 0 && mismatchValues
.length
=== 0);
1179 jasmine
.Env
.prototype.equals_ = function(a
, b
, mismatchKeys
, mismatchValues
) {
1180 mismatchKeys
= mismatchKeys
|| [];
1181 mismatchValues
= mismatchValues
|| [];
1183 for (var i
= 0; i
< this.equalityTesters_
.length
; i
++) {
1184 var equalityTester
= this.equalityTesters_
[i
];
1185 var result
= equalityTester(a
, b
, this, mismatchKeys
, mismatchValues
);
1186 if (result
!== jasmine
.undefined) return result
;
1189 if (a
=== b
) return true;
1191 if (a
=== jasmine
.undefined || a
=== null || b
=== jasmine
.undefined || b
=== null) {
1192 return (a
== jasmine
.undefined && b
== jasmine
.undefined);
1195 if (jasmine
.isDomNode(a
) && jasmine
.isDomNode(b
)) {
1199 if (a
instanceof Date
&& b
instanceof Date
) {
1200 return a
.getTime() == b
.getTime();
1203 if (a
.jasmineMatches
) {
1204 return a
.jasmineMatches(b
);
1207 if (b
.jasmineMatches
) {
1208 return b
.jasmineMatches(a
);
1211 if (a
instanceof jasmine
.Matchers
.ObjectContaining
) {
1212 return a
.matches(b
);
1215 if (b
instanceof jasmine
.Matchers
.ObjectContaining
) {
1216 return b
.matches(a
);
1219 if (jasmine
.isString_(a
) && jasmine
.isString_(b
)) {
1223 if (jasmine
.isNumber_(a
) && jasmine
.isNumber_(b
)) {
1227 if (a
instanceof RegExp
&& b
instanceof RegExp
) {
1228 return this.compareRegExps_(a
, b
, mismatchKeys
, mismatchValues
);
1231 if (typeof a
=== "object" && typeof b
=== "object") {
1232 return this.compareObjects_(a
, b
, mismatchKeys
, mismatchValues
);
1239 jasmine
.Env
.prototype.contains_ = function(haystack
, needle
) {
1240 if (jasmine
.isArray_(haystack
)) {
1241 for (var i
= 0; i
< haystack
.length
; i
++) {
1242 if (this.equals_(haystack
[i
], needle
)) return true;
1246 return haystack
.indexOf(needle
) >= 0;
1249 jasmine
.Env
.prototype.addEqualityTester = function(equalityTester
) {
1250 this.equalityTesters_
.push(equalityTester
);
1253 * Basic browsers detection.
1255 jasmine
.browser
= {};
1256 jasmine
.browser
.isIE
= !!window
.ActiveXObject
;
1257 jasmine
.browser
.isIE6
= jasmine
.browser
.isIE
&& !window
.XMLHttpRequest
;
1258 jasmine
.browser
.isIE7
= jasmine
.browser
.isIE
&& !!window
.XMLHttpRequest
&& !document
.documentMode
;
1259 jasmine
.browser
.isIE8
= jasmine
.browser
.isIE
&& !!window
.XMLHttpRequest
&& !!document
.documentMode
&& !window
.performance
;
1260 jasmine
.browser
.isIE9
= jasmine
.browser
.isIE
&& !!window
.performance
;
1261 jasmine
.browser
.isSafari3
= /safari/.test(navigator
.userAgent
.toLowerCase()) && /version\/3/.test(navigator
.userAgent
.toLowerCase());
1262 jasmine
.browser
.isOpera
= !!window
.opera
;
1263 jasmine
.browser
.isOpera11
= jasmine
.browser
.isOpera
&& parseInt(window
.opera
.version(), 10) > 10;
1268 * Checks whether or not the specified item exists in the array.
1269 * Array.prototype.indexOf is missing in Internet Explorer, unfortunately.
1270 * We always have to use this static method instead for consistency
1271 * @param {Array} array The array to check
1272 * @param {Mixed} item The item to look for
1273 * @param {Number} from (Optional) The index at which to begin the search
1274 * @return {Number} The index of item in the array (or -1 if it is not found)
1276 jasmine
.array
.indexOf = function(array
, item
, from){
1277 if (array
.indexOf
) {
1278 return array
.indexOf(item
, from);
1281 var i
, length
= array
.length
;
1283 for (i
= (from < 0) ? Math
.max(0, length
+ from) : from || 0; i
< length
; i
++){
1284 if (array
[i
] === item
) {
1293 * Removes the specified item from the array. If the item is not found nothing happens.
1294 * @param {Array} array The array
1295 * @param {Mixed} item The item to remove
1296 * @return {Array} The passed array itself
1298 jasmine
.array
.remove = function(array
, item
) {
1299 var index
= this.indexOf(array
, item
);
1302 array
.splice(index
, 1);
1308 jasmine
.Env
.prototype.it = function(description
, func
, timeout
) {
1309 var spec
= new jasmine
.Spec(this, this.currentSuite
, description
);
1310 this.currentSuite
.add(spec
);
1311 this.currentSpec
= spec
;
1315 func
.typeName
= 'it';
1316 var block
= new jasmine
.Block(spec
.env
, func
, spec
);
1317 block
.timeout
= parseInt(timeout
);
1318 spec
.addToQueue(block
);
1325 jasmine
.Env
.prototype.specFor = function(object
, specForFn
) {
1329 for (property
in object
) {
1330 if (!object
.hasOwnProperty(property
)) {
1333 specForFn
.call(this, property
, object
[property
], index
, object
);
1338 jasmine
.Env
.prototype.xspecFor = function(object
, specForFn
) {};/** No-op base class for Jasmine reporters.
1342 jasmine
.Reporter = function() {
1345 //noinspection JSUnusedLocalSymbols
1346 jasmine
.Reporter
.prototype.reportRunnerStarting = function(runner
) {
1349 //noinspection JSUnusedLocalSymbols
1350 jasmine
.Reporter
.prototype.reportRunnerResults = function(runner
) {
1353 //noinspection JSUnusedLocalSymbols
1354 jasmine
.Reporter
.prototype.reportSuiteResults = function(suite
) {
1357 //noinspection JSUnusedLocalSymbols
1358 jasmine
.Reporter
.prototype.reportSpecStarting = function(spec
) {
1361 //noinspection JSUnusedLocalSymbols
1362 jasmine
.Reporter
.prototype.reportSpecResults = function(spec
) {
1365 //noinspection JSUnusedLocalSymbols
1366 jasmine
.Reporter
.prototype.log = function(str
) {
1370 * Blocks are functions with executable code that make up a spec.
1373 * @param {jasmine.Env} env
1374 * @param {Function} func
1375 * @param {jasmine.Spec} spec
1377 jasmine
.Block = function(env
, func
, spec
) {
1383 jasmine
.Block
.prototype.execute = function(onComplete
) {
1384 if (!jasmine
.CATCH_EXCEPTIONS
) {
1385 this.func
.apply(this.spec
);
1389 this.func
.apply(this.spec
);
1396 jasmine
.Block
.prototype.execute = function(onComplete
) {
1397 if (this.func
.length
=== 1) {
1399 var timeOutId
= setTimeout(function(){
1401 }, this.timeout
|| jasmine
.DEFAULT_TIMEOUT_INTERVAL
);
1403 if (!jasmine
.CATCH_EXCEPTIONS
) {
1404 this.func
.call(this.spec
, function() {
1405 clearTimeout(timeOutId
);
1410 this.func
.call(this.spec
, function() {
1411 clearTimeout(timeOutId
);
1420 if (!jasmine
.CATCH_EXCEPTIONS
) {
1421 this.func
.apply(this.spec
);
1424 this.func
.apply(this.spec
);
1431 };/** JavaScript API reporter.
1435 jasmine
.JsApiReporter = function() {
1436 this.started
= false;
1437 this.finished
= false;
1442 jasmine
.JsApiReporter
.prototype.reportRunnerStarting = function(runner
) {
1443 this.started
= true;
1444 var suites
= runner
.topLevelSuites();
1445 for (var i
= 0; i
< suites
.length
; i
++) {
1446 var suite
= suites
[i
];
1447 this.suites_
.push(this.summarize_(suite
));
1451 jasmine
.JsApiReporter
.prototype.suites = function() {
1452 return this.suites_
;
1455 jasmine
.JsApiReporter
.prototype.summarize_ = function(suiteOrSpec
) {
1456 var isSuite
= suiteOrSpec
instanceof jasmine
.Suite
;
1459 name
: suiteOrSpec
.description
,
1460 type
: isSuite
? 'suite' : 'spec',
1465 var children
= suiteOrSpec
.children();
1466 for (var i
= 0; i
< children
.length
; i
++) {
1467 summary
.children
.push(this.summarize_(children
[i
]));
1473 jasmine
.JsApiReporter
.prototype.results = function() {
1474 return this.results_
;
1477 jasmine
.JsApiReporter
.prototype.resultsForSpec = function(specId
) {
1478 return this.results_
[specId
];
1481 //noinspection JSUnusedLocalSymbols
1482 jasmine
.JsApiReporter
.prototype.reportRunnerResults = function(runner
) {
1483 this.finished
= true;
1486 //noinspection JSUnusedLocalSymbols
1487 jasmine
.JsApiReporter
.prototype.reportSuiteResults = function(suite
) {
1490 //noinspection JSUnusedLocalSymbols
1491 jasmine
.JsApiReporter
.prototype.reportSpecResults = function(spec
) {
1492 this.results_
[spec
.id
] = {
1493 messages
: spec
.results().getItems(),
1494 result
: spec
.results().failedCount
> 0 ? "failed" : "passed"
1498 //noinspection JSUnusedLocalSymbols
1499 jasmine
.JsApiReporter
.prototype.log = function(str
) {
1502 jasmine
.JsApiReporter
.prototype.resultsForSpecs = function(specIds
){
1504 for (var i
= 0; i
< specIds
.length
; i
++) {
1505 var specId
= specIds
[i
];
1506 results
[specId
] = this.summarizeResult_(this.results_
[specId
]);
1511 jasmine
.JsApiReporter
.prototype.summarizeResult_ = function(result
){
1512 var summaryMessages
= [];
1513 var messagesLength
= result
.messages
.length
;
1514 for (var messageIndex
= 0; messageIndex
< messagesLength
; messageIndex
++) {
1515 var resultMessage
= result
.messages
[messageIndex
];
1516 summaryMessages
.push({
1517 text
: resultMessage
.type
== 'log' ? resultMessage
.toString() : jasmine
.undefined,
1518 passed
: resultMessage
.passed
? resultMessage
.passed() : true,
1519 type
: resultMessage
.type
,
1520 message
: resultMessage
.message
,
1522 stack
: resultMessage
.passed
&& !resultMessage
.passed() ? resultMessage
.trace
.stack
: jasmine
.undefined
1528 result
: result
.result
,
1529 messages
: summaryMessages
1535 * @param {jasmine.Env} env
1537 * @param {jasmine.Spec} spec
1539 jasmine
.Matchers = function(env
, actual
, spec
, opt_isNot
) {
1541 this.actual
= actual
;
1543 this.isNot
= opt_isNot
|| false;
1544 this.reportWasCalled_
= false;
1547 // todo: @deprecated as of Jasmine 0.11, remove soon [xw]
1548 jasmine
.Matchers
.pp = function(str
) {
1549 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");
1552 // todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]
1553 jasmine
.Matchers
.prototype.report = function(result
, failing_message
, details
) {
1554 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");
1557 jasmine
.Matchers
.wrapInto_ = function(prototype, matchersClass
) {
1558 for (var methodName
in prototype) {
1559 if (methodName
== 'report') continue;
1560 var orig
= prototype[methodName
];
1561 matchersClass
.prototype[methodName
] = jasmine
.Matchers
.matcherFn_(methodName
, orig
);
1565 jasmine
.Matchers
.matcherFn_ = function(matcherName
, matcherFunction
) {
1567 var matcherArgs
= jasmine
.util
.argsToArray(arguments
);
1568 var result
= matcherFunction
.apply(this, arguments
);
1574 if (this.reportWasCalled_
) return result
;
1579 message
= this.message
.apply(this, arguments
);
1580 if (jasmine
.isArray_(message
)) {
1581 message
= message
[this.isNot
? 1 : 0];
1584 var englishyPredicate
= matcherName
.replace(/[A-Z]/g, function(s
) { return ' ' + s
.toLowerCase(); });
1585 message
= "Expected " + jasmine
.pp(this.actual
) + (this.isNot
? " not " : " ") + englishyPredicate
;
1586 if (matcherArgs
.length
> 0) {
1587 for (var i
= 0; i
< matcherArgs
.length
; i
++) {
1588 if (i
> 0) message
+= ",";
1589 message
+= " " + jasmine
.pp(matcherArgs
[i
]);
1595 var expectationResult
= new jasmine
.ExpectationResult({
1596 matcherName
: matcherName
,
1598 expected
: matcherArgs
.length
> 1 ? matcherArgs
: matcherArgs
[0],
1599 actual
: this.actual
,
1602 this.spec
.addMatcherResult(expectationResult
);
1603 return jasmine
.undefined;
1611 * toBe: compares the actual to the expected using ===
1614 jasmine
.Matchers
.prototype.toBe = function(expected
) {
1615 return this.actual
=== expected
;
1619 * toNotBe: compares the actual to the expected using !==
1621 * @deprecated as of 1.0. Use not.toBe() instead.
1623 jasmine
.Matchers
.prototype.toNotBe = function(expected
) {
1624 return this.actual
!== expected
;
1628 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.
1632 jasmine
.Matchers
.prototype.toEqual = function(expected
) {
1633 return this.env
.equals_(this.actual
, expected
);
1637 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual
1639 * @deprecated as of 1.0. Use not.toEqual() instead.
1641 jasmine
.Matchers
.prototype.toNotEqual = function(expected
) {
1642 return !this.env
.equals_(this.actual
, expected
);
1646 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes
1647 * a pattern or a String.
1651 jasmine
.Matchers
.prototype.toMatch = function(expected
) {
1652 return new RegExp(expected
).test(this.actual
);
1656 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch
1658 * @deprecated as of 1.0. Use not.toMatch() instead.
1660 jasmine
.Matchers
.prototype.toNotMatch = function(expected
) {
1661 return !(new RegExp(expected
).test(this.actual
));
1665 * Matcher that compares the actual to jasmine.undefined.
1667 jasmine
.Matchers
.prototype.toBeDefined = function() {
1668 return (this.actual
!== jasmine
.undefined);
1672 * Matcher that compares the actual to jasmine.undefined.
1674 jasmine
.Matchers
.prototype.toBeUndefined = function() {
1675 return (this.actual
=== jasmine
.undefined);
1679 * Matcher that compares the actual to null.
1681 jasmine
.Matchers
.prototype.toBeNull = function() {
1682 return (this.actual
=== null);
1686 * Matcher that compares the actual to NaN.
1688 jasmine
.Matchers
.prototype.toBeNaN = function() {
1689 this.message = function() {
1690 return [ "Expected " + jasmine
.pp(this.actual
) + " to be NaN." ];
1693 return (this.actual
!== this.actual
);
1697 * Matcher that boolean not-nots the actual.
1699 jasmine
.Matchers
.prototype.toBeTruthy = function() {
1700 return !!this.actual
;
1705 * Matcher that boolean nots the actual.
1707 jasmine
.Matchers
.prototype.toBeFalsy = function() {
1708 return !this.actual
;
1713 * Matcher that checks to see if the actual, a Jasmine spy, was called.
1715 jasmine
.Matchers
.prototype.toHaveBeenCalled = function() {
1716 if (arguments
.length
> 0) {
1717 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');
1720 if (!jasmine
.isSpy(this.actual
)) {
1721 throw new Error('Expected a spy, but got ' + jasmine
.pp(this.actual
) + '.');
1724 this.message = function() {
1726 "Expected spy " + this.actual
.identity
+ " to have been called.",
1727 "Expected spy " + this.actual
.identity
+ " not to have been called."
1731 return this.actual
.wasCalled
;
1734 /** @deprecated Use expect(xxx).toHaveBeenCalled() instead */
1735 jasmine
.Matchers
.prototype.wasCalled
= jasmine
.Matchers
.prototype.toHaveBeenCalled
;
1738 * Matcher that checks to see if the actual, a Jasmine spy, was not called.
1740 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead
1742 jasmine
.Matchers
.prototype.wasNotCalled = function() {
1743 if (arguments
.length
> 0) {
1744 throw new Error('wasNotCalled does not take arguments');
1747 if (!jasmine
.isSpy(this.actual
)) {
1748 throw new Error('Expected a spy, but got ' + jasmine
.pp(this.actual
) + '.');
1751 this.message = function() {
1753 "Expected spy " + this.actual
.identity
+ " to not have been called.",
1754 "Expected spy " + this.actual
.identity
+ " to have been called."
1758 return !this.actual
.wasCalled
;
1762 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.
1767 jasmine
.Matchers
.prototype.toHaveBeenCalledWith = function() {
1768 var expectedArgs
= jasmine
.util
.argsToArray(arguments
);
1769 if (!jasmine
.isSpy(this.actual
)) {
1770 throw new Error('Expected a spy, but got ' + jasmine
.pp(this.actual
) + '.');
1772 this.message = function() {
1773 var invertedMessage
= "Expected spy " + this.actual
.identity
+ " not to have been called with " + jasmine
.pp(expectedArgs
) + " but it was.";
1774 var positiveMessage
= "";
1775 if (this.actual
.callCount
=== 0) {
1776 positiveMessage
= "Expected spy " + this.actual
.identity
+ " to have been called with " + jasmine
.pp(expectedArgs
) + " but it was never called.";
1778 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, '')
1780 return [positiveMessage
, invertedMessage
];
1783 return this.env
.contains_(this.actual
.argsForCall
, expectedArgs
);
1786 /** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */
1787 jasmine
.Matchers
.prototype.wasCalledWith
= jasmine
.Matchers
.prototype.toHaveBeenCalledWith
;
1789 /** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */
1790 jasmine
.Matchers
.prototype.wasNotCalledWith = function() {
1791 var expectedArgs
= jasmine
.util
.argsToArray(arguments
);
1792 if (!jasmine
.isSpy(this.actual
)) {
1793 throw new Error('Expected a spy, but got ' + jasmine
.pp(this.actual
) + '.');
1796 this.message = function() {
1798 "Expected spy not to have been called with " + jasmine
.pp(expectedArgs
) + " but it was",
1799 "Expected spy to have been called with " + jasmine
.pp(expectedArgs
) + " but it was"
1803 return !this.env
.contains_(this.actual
.argsForCall
, expectedArgs
);
1807 * Matcher that checks that the expected item is an element in the actual Array.
1809 * @param {Object} expected
1811 jasmine
.Matchers
.prototype.toContain = function(expected
) {
1812 return this.env
.contains_(this.actual
, expected
);
1816 * Matcher that checks that the expected item is NOT an element in the actual Array.
1818 * @param {Object} expected
1819 * @deprecated as of 1.0. Use not.toContain() instead.
1821 jasmine
.Matchers
.prototype.toNotContain = function(expected
) {
1822 return !this.env
.contains_(this.actual
, expected
);
1825 jasmine
.Matchers
.prototype.toBeLessThan = function(expected
) {
1826 return this.actual
< expected
;
1829 jasmine
.Matchers
.prototype.toBeGreaterThan = function(expected
) {
1830 return this.actual
> expected
;
1834 * Matcher that checks that the expected item is equal to the actual item
1835 * up to a given level of decimal precision (default 2).
1837 * @param {Number} expected
1838 * @param {Number} precision, as number of decimal places
1840 jasmine
.Matchers
.prototype.toBeCloseTo = function(expected
, precision
) {
1841 if (!(precision
=== 0)) {
1842 precision
= precision
|| 2;
1844 return Math
.abs(expected
- this.actual
) < (Math
.pow(10, -precision
) / 2);
1848 * Matcher that checks that the expected exception was thrown by the actual.
1850 * @param {String} [expected]
1852 jasmine
.Matchers
.prototype.toThrow = function(expected
) {
1855 if (typeof this.actual
!= 'function') {
1856 throw new Error('Actual is not a function');
1864 result
= (expected
=== jasmine
.undefined || this.env
.equals_(exception
.message
|| exception
, expected
.message
|| expected
));
1867 var not
= this.isNot
? "not " : "";
1869 this.message = function() {
1870 if (exception
&& (expected
=== jasmine
.undefined || !this.env
.equals_(exception
.message
|| exception
, expected
.message
|| expected
))) {
1871 return ["Expected function " + not
+ "to throw", expected
? expected
.message
|| expected
: "an exception", ", but it threw", exception
.message
|| exception
].join(' ');
1873 return "Expected function to throw an exception.";
1880 jasmine
.Matchers
.Any = function(expectedClass
) {
1881 this.expectedClass
= expectedClass
;
1884 jasmine
.Matchers
.Any
.prototype.jasmineMatches = function(other
) {
1885 if (this.expectedClass
== String
) {
1886 return typeof other
== 'string' || other
instanceof String
;
1889 if (this.expectedClass
== Number
) {
1890 return typeof other
== 'number' || other
instanceof Number
;
1893 if (this.expectedClass
== Function
) {
1894 return typeof other
== 'function' || other
instanceof Function
;
1897 if (this.expectedClass
== Object
) {
1898 return typeof other
== 'object';
1901 return other
instanceof this.expectedClass
;
1904 jasmine
.Matchers
.Any
.prototype.jasmineToString = function() {
1905 return '<jasmine.any(' + this.expectedClass
+ ')>';
1908 jasmine
.Matchers
.ObjectContaining = function (sample
) {
1909 this.sample
= sample
;
1912 jasmine
.Matchers
.ObjectContaining
.prototype.jasmineMatches = function(other
, mismatchKeys
, mismatchValues
) {
1913 mismatchKeys
= mismatchKeys
|| [];
1914 mismatchValues
= mismatchValues
|| [];
1916 var env
= jasmine
.getEnv();
1918 var hasKey = function(obj
, keyName
) {
1919 return obj
!= null && obj
[keyName
] !== jasmine
.undefined;
1922 for (var property
in this.sample
) {
1923 if (!hasKey(other
, property
) && hasKey(this.sample
, property
)) {
1924 mismatchKeys
.push("expected has key '" + property
+ "', but missing from actual.");
1926 else if (!env
.equals_(this.sample
[property
], other
[property
], mismatchKeys
, mismatchValues
)) {
1927 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.");
1931 return (mismatchKeys
.length
=== 0 && mismatchValues
.length
=== 0);
1934 jasmine
.Matchers
.ObjectContaining
.prototype.jasmineToString = function () {
1935 return "<jasmine.objectContaining(" + jasmine
.pp(this.sample
) + ")>";
1938 * Override of toThrow special opera 10 !!!
1940 jasmine
.Matchers
.prototype.toThrow = function(expected
) {
1943 if (typeof this.actual
!= 'function') {
1944 throw new Error('Actual is not a function');
1947 // mock the console to avoid logging to the real console during the tests
1948 var global
= Ext
.global
;
1958 error: function(s
) {
1967 // This is to allow setting breakpoints for console messages
1968 // that are not expected to be suppressed by jasmine.toThrow and alike
1969 Ext
.global
.console
.dir
.$emptyFn
= Ext
.global
.console
.log
.$emptyFn
= true;
1970 Ext
.global
.console
.error
.$emptyFn
= Ext
.global
.console
.warn
.$emptyFn
= true;
1978 result
= (expected
=== jasmine
.undefined || this.env
.contains_(exception
.message
|| exception
, expected
.message
|| expected
));
1981 Ext
.global
= global
;
1983 var not
= this.isNot
? "not " : "";
1985 this.message = function() {
1986 if (exception
&& (expected
=== jasmine
.undefined || !this.env
.contains_(exception
.message
|| exception
, expected
.message
|| expected
))) {
1987 return ["Expected function " + not
+ "to throw", expected
? expected
.message
|| expected
: " an exception", ", but it threw", exception
.message
|| exception
].join(' ');
1989 return "Expected function to throw an exception.";
1996 jasmine
.Matchers
.prototype.toRaiseExtError = function(expected
) {
1998 global
= Ext
.global
,
2000 if (typeof this.actual
!= 'function') {
2001 throw new Error('Actual is not a function');
2004 // mock the console to avoid logging to the real console during the tests
2013 error: function(s
) {
2022 // This is to allow setting breakpoints for console messages
2023 // that are not expected to be suppressed by jasmine.toThrow and alike
2024 Ext
.global
.console
.dir
.$emptyFn
= Ext
.global
.console
.log
.$emptyFn
= true;
2025 Ext
.global
.console
.error
.$emptyFn
= Ext
.global
.console
.warn
.$emptyFn
= true;
2034 Ext
.global
= global
;
2036 if (extError
&& extError
instanceof Error
) {
2037 result
= (expected
=== jasmine
.undefined || this.env
.contains_(extError
.toString(), expected
.message
|| expected
));
2041 var not
= this.isNot
? "not " : "";
2043 this.message = function() {
2044 if (!extError
instanceof Error
) {
2045 return "Exception thrown is not an instance of Ext.Error";
2046 } else if (extError
&& (expected
=== jasmine
.undefined || !this.env
.contains_(extError
.toString(), expected
.message
|| expected
))) {
2047 return ["Expected function " + not
+ "to throw", expected
? expected
.message
|| expected
: " an exception", ", but it threw", extError
.toString()].join(' ');
2049 return "Expected function to throw an exception.";
2056 jasmine
.Matchers
.prototype.hasHTML = function(expected
) {
2059 if (!me
.actual
|| !me
.actual
.tagName
) {
2060 throw new Error('Actual is not a dom element');
2062 if (jasmine
.browser
.isSafari3
) {
2063 expected
= expected
.replace(/>/g, '>');
2065 // this normalize innerHTML which could vary a lot
2066 var normalizedHTML
= me
.actual
.innerHTML
.replace(/<[^>]*>/g, function(match1
) {
2067 return match1
.toLowerCase().replace(/=\w+/g, function(match2
) {
2068 return '="' + match2
.split('=')[1] + '"';
2072 me
.message = function() {
2074 "Expected dom element innerHTML to be " + expected
+ " but was " + normalizedHTML
,
2075 "Expected dom element innerHTML to not be " + expected
+ "."
2079 return normalizedHTML
=== expected
;
2082 jasmine
.Matchers
.prototype.toHaveCls = function(cls
) {
2083 return Ext
.fly(this.actual
).hasCls(cls
);
2086 jasmine
.Matchers
.prototype.toEqualTime = function(hour
, minute
, second
, ms
) {
2087 var actual
= this.actual
;
2088 return actual
instanceof Date
&&
2089 actual
.getHours() === hour
&&
2090 actual
.getMinutes() === (minute
|| 0) &&
2091 actual
.getSeconds() === (second
|| 0) &&
2092 actual
.getMilliseconds() === (ms
|| 0);
2096 jasmine
.Matchers
.prototype.toBePositionedAt = function(x
, y
) {
2097 var xy
= this.actual
.getXY();
2098 this.message = function() {
2099 return "Expected Ext.Element to be positioned at (" + x
+ "," + y
+ ") but was positioned at (" + xy
[0] + "," + xy
[1] + ")";
2101 return xy
[0] === x
&& xy
[1] === y
;
2105 var elementPropGetters
= {
2106 x: function (el
, root
) {
2108 x0
= root
? root
.el
.getX() : el
.getX();
2111 y: function (el
, root
) {
2113 y0
= root
? root
.el
.getY() : el
.getY();
2117 return el
.getWidth();
2120 return el
.getHeight();
2122 xywh: function(el
, root
) {
2124 x0
= root
? root
.el
.getX() : el
.getX(),
2126 y0
= root
? root
.el
.getY() : el
.getY(),
2130 dims
.push(x
- x0
, y
- y0
, w
, h
);
2131 return dims
.join(' ');
2133 cls: function (el
) {
2134 return el
.dom
.className
;
2138 "IE6", "IE7", "IE8", "IE9", "IE",
2139 "Gecko3", "Gecko4", "Gecko5", "Gecko10", "Gecko",
2140 "FF3_6", "FF4", "FF5",
2142 "Safari2", "Safari3", "Safari4", "Safari5", "Safari"
2144 blen
= browsers
.length
,
2146 browserCheck = function(expected
){
2147 if(Ext
.isNumeric(expected
) || Ext
.isArray(expected
)) {
2150 for (b
= 0; b
< blen
; b
++) {
2151 browser
= browsers
[b
];
2152 if (expected
.hasOwnProperty(browser
) && Ext
["is" + browser
]){
2153 return expected
[browser
];
2156 return expected
['*'] || expected
;
2158 layoutFly
= new Ext
.dom
.Fly();
2161 function checkLayout (comp
, layout
, root
, path
) {
2162 Ext
.Object
.each(layout
, function (name
, value
) {
2163 if (name
== 'items' || name
== 'dockedItems') {
2164 Ext
.Object
.each(value
, function (id
, sub
) {
2165 var isNum
= String(parseInt(id
,10)) == id
,
2166 child
= isNum
? comp
[name
].items
[parseInt(id
,10)]
2167 : (comp
.getComponent(id
) || comp
.child(id
));
2170 id
= '.' + name
+ '[' + id
+ ']';
2171 } else if (id
.charAt(0) != ':') {
2176 checkLayout(child
, sub
, comp
, path
+ id
);
2178 expect(id
).toBe('found!');
2182 // the name is an element name like 'body'
2183 var el
= comp
[name
];
2186 // no child el matched, assume the key is a CSS selector
2187 el
= layoutFly
.attach(comp
.el
.selectNode(name
, true));
2190 if (el
.isComponent
) {
2191 checkLayout(el
, value
, el
.ownerCt
, path
+ '_' + name
);
2192 } else if (el
.dom
) {
2193 value
= browserCheck(value
);
2195 var dims
= value
.xywh
.split(' ');
2196 value
.x
= eval('(' + dims
[0] + ')');
2197 value
.y
= eval('(' + dims
[1] + ')');
2198 value
.w
= eval('(' + dims
[2] + ')');
2199 value
.h
= eval('(' + dims
[3] + ')');
2202 Ext
.Object
.each(value
, function (prop
, expected
) {
2203 var actual
= elementPropGetters
[prop
](el
, root
|| comp
.el
),
2204 pfx
= (path
? path
+ '.' : '') + name
+ '.' + prop
+ '=';
2206 if (Ext
.isArray(expected
)) {
2207 if (actual
< expected
[0] || actual
> expected
[1]) {
2208 expect(pfx
+ '=' + actual
).
2209 toBe('in [' + expected
[0] + ',' + expected
[1] + ']');
2211 } else if (actual
!= expected
) {
2212 expect(pfx
+ actual
).toEqual(expected
);
2220 jasmine
.Matchers
.prototype.toHaveLayout = function(expected
) {
2221 var comp
= this.actual
;
2222 checkLayout(comp
, expected
, comp
.ownerCt
, comp
.getXType());
2226 jasmine
.Matchers
.prototype.toBeLessThanOrEqual = function(expected
) {
2227 return this.actual
<= expected
;
2230 jasmine
.Matchers
.prototype.toBeGreaterThanOrEqual = function(expected
) {
2231 return this.actual
>= expected
;
2234 jasmine
.Matchers
.prototype.toBeGE
= jasmine
.Matchers
.prototype.toBeAtLeast
= jasmine
.Matchers
.prototype.toBeGreaterThanOrEqual
;
2235 jasmine
.Matchers
.prototype.toBeLE
= jasmine
.Matchers
.prototype.toBeLessThanOrEqual
;
2236 jasmine
.Matchers
.prototype.toBeLT
= jasmine
.Matchers
.prototype.toBeLessThan
;
2237 jasmine
.Matchers
.prototype.toBeGT
= jasmine
.Matchers
.prototype.toBeGreaterThan
;
2241 jasmine
.Matchers
.prototype.toHaveFiredEvents = function() {
2242 var calls
= this.actual
.fireEvent
.calls
,
2245 expectedEvents
= Array
.prototype.slice
.call(arguments
, 0),
2246 length
= expectedEvents
.length
,
2253 for (;i
< length
; i
++) {
2254 expectedEvent
= expectedEvents
[i
];
2256 actualEvent
= calls
[i
].args
[0];
2261 actualEvents
.push(actualEvent
);
2264 if (actualEvent
!= expectedEvent
) {
2269 this.message = function() {
2270 return "Expected events flow to be (" + expectedEvents
.length
+ " events): \n" + expectedEvents
.join('\n') + "\nBut it was (" + actualEvents
.length
+ " events): \n"+ actualEvents
.join('\n');
2275 jasmine
.Matchers
.prototype.toBeApprox = function(expected
, errorMargin
) {
2276 errorMargin
= errorMargin
|| 1;
2278 var min
= expected
- errorMargin
,
2279 max
= expected
+ errorMargin
;
2281 this.message = function() {
2282 return "Expected " + this.actual
+ " to be approximately " + expected
+ " by " + errorMargin
;
2284 return this.actual
>= min
&& this.actual
<= max
;
2287 jasmine
.Matchers
.prototype.toBeWithin = function(deviation
, value
) {
2288 var actual
= this.actual
;
2290 if (deviation
> 0) {
2291 return actual
>= (value
- deviation
) && actual
<= (value
+ deviation
);
2294 return actual
>= (value
+ deviation
) && actual
<= (value
- deviation
);
2300 jasmine
.MultiReporter = function() {
2301 this.subReporters_
= [];
2303 jasmine
.util
.inherit(jasmine
.MultiReporter
, jasmine
.Reporter
);
2305 jasmine
.MultiReporter
.prototype.addReporter = function(reporter
) {
2306 this.subReporters_
.push(reporter
);
2310 var functionNames
= [
2311 "reportRunnerStarting",
2312 "reportRunnerResults",
2313 "reportSuiteStarting",
2314 "reportSuiteResults",
2315 "reportSpecStarting",
2316 "reportSpecResults",
2319 for (var i
= 0; i
< functionNames
.length
; i
++) {
2320 var functionName
= functionNames
[i
];
2321 jasmine
.MultiReporter
.prototype[functionName
] = (function(functionName
) {
2323 for (var j
= 0; j
< this.subReporters_
.length
; j
++) {
2324 var subReporter
= this.subReporters_
[j
];
2325 if (subReporter
[functionName
]) {
2326 subReporter
[functionName
].apply(subReporter
, arguments
);
2333 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults
2337 jasmine
.NestedResults = function() {
2339 * The total count of results
2341 this.totalCount
= 0;
2343 * Number of passed results
2345 this.passedCount
= 0;
2347 * Number of failed results
2349 this.failedCount
= 0;
2351 * Was this suite/spec skipped?
2353 this.skipped
= false;
2361 * Roll up the result counts.
2365 jasmine
.NestedResults
.prototype.rollupCounts = function(result
) {
2366 this.totalCount
+= result
.totalCount
;
2367 this.passedCount
+= result
.passedCount
;
2368 this.failedCount
+= result
.failedCount
;
2372 * Adds a log message.
2373 * @param values Array of message parts which will be concatenated later.
2375 jasmine
.NestedResults
.prototype.log = function(values
) {
2376 this.items_
.push(new jasmine
.MessageResult(values
));
2380 * Getter for the results: message & results.
2382 jasmine
.NestedResults
.prototype.getItems = function() {
2387 * Adds a result, tracking counts (total, passed, & failed)
2388 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result
2390 jasmine
.NestedResults
.prototype.addResult = function(result
) {
2391 if (result
.type
!= 'log') {
2392 if (result
.items_
) {
2393 this.rollupCounts(result
);
2396 if (result
.passed()) {
2403 this.items_
.push(result
);
2407 * @returns {Boolean} True if <b>everything</b> below passed
2409 jasmine
.NestedResults
.prototype.passed = function() {
2410 return this.passedCount
=== this.totalCount
;
2413 jasmine
.NestedResults
.prototype.cleanup = function() {
2417 * Base class for pretty printing for expectation results.
2419 jasmine
.PrettyPrinter = function() {
2420 this.ppNestLevel_
= 0;
2424 * Formats a value in a nice, human-readable string.
2428 jasmine
.PrettyPrinter
.prototype.format = function(value
) {
2429 this.ppNestLevel_
++;
2431 if (value
=== jasmine
.undefined) {
2432 this.emitScalar('undefined');
2433 } else if (value
=== null) {
2434 this.emitScalar('null');
2435 } else if (value
=== jasmine
.getGlobal()) {
2436 this.emitScalar('<global>');
2437 } else if (value
.jasmineToString
) {
2438 this.emitScalar(value
.jasmineToString());
2439 } else if (typeof value
=== 'string') {
2440 this.emitString(value
);
2441 } else if (jasmine
.isSpy(value
)) {
2442 this.emitScalar("spy on " + value
.identity
);
2443 } else if (value
instanceof RegExp
) {
2444 this.emitScalar(value
.toString());
2445 } else if (typeof value
=== 'function') {
2446 this.emitScalar('Function');
2447 } else if (typeof value
.nodeType
=== 'number') {
2448 this.emitScalar('HTMLNode');
2449 } else if (value
instanceof Date
) {
2450 this.emitScalar('Date(' + value
+ ')');
2451 } else if (value
.__Jasmine_been_here_before__
) {
2452 this.emitScalar('<circular reference: ' + (jasmine
.isArray_(value
) ? 'Array' : 'Object') + '>');
2453 } else if (jasmine
.isArray_(value
) || typeof value
== 'object') {
2454 value
.__Jasmine_been_here_before__
= true;
2455 if (jasmine
.isArray_(value
)) {
2456 this.emitArray(value
);
2458 this.emitObject(value
);
2460 delete value
.__Jasmine_been_here_before__
;
2462 this.emitScalar(value
.toString());
2465 this.ppNestLevel_
--;
2469 jasmine
.PrettyPrinter
.prototype.iterateObject = function(obj
, fn
) {
2470 for (var property
in obj
) {
2471 if (!obj
.hasOwnProperty(property
)) continue;
2472 if (property
== '__Jasmine_been_here_before__') continue;
2473 fn(property
, obj
.__lookupGetter__
? (obj
.__lookupGetter__(property
) !== jasmine
.undefined &&
2474 obj
.__lookupGetter__(property
) !== null) : false);
2478 jasmine
.PrettyPrinter
.prototype.emitArray
= jasmine
.unimplementedMethod_
;
2479 jasmine
.PrettyPrinter
.prototype.emitObject
= jasmine
.unimplementedMethod_
;
2480 jasmine
.PrettyPrinter
.prototype.emitScalar
= jasmine
.unimplementedMethod_
;
2481 jasmine
.PrettyPrinter
.prototype.emitString
= jasmine
.unimplementedMethod_
;
2483 jasmine
.StringPrettyPrinter = function() {
2484 jasmine
.PrettyPrinter
.call(this);
2488 jasmine
.util
.inherit(jasmine
.StringPrettyPrinter
, jasmine
.PrettyPrinter
);
2490 jasmine
.StringPrettyPrinter
.prototype.emitScalar = function(value
) {
2494 jasmine
.StringPrettyPrinter
.prototype.emitString = function(value
) {
2495 this.append("'" + value
+ "'");
2498 jasmine
.StringPrettyPrinter
.prototype.emitArray = function(array
) {
2499 if (this.ppNestLevel_
> jasmine
.MAX_PRETTY_PRINT_DEPTH
) {
2500 this.append("Array");
2505 for (var i
= 0; i
< array
.length
; i
++) {
2509 this.format(array
[i
]);
2514 jasmine
.StringPrettyPrinter
.prototype.emitObject = function(obj
) {
2515 if (this.ppNestLevel_
> jasmine
.MAX_PRETTY_PRINT_DEPTH
) {
2516 this.append("Object");
2524 this.iterateObject(obj
, function(property
, isGetter
) {
2531 self
.append(property
);
2534 self
.append('<getter>');
2536 self
.format(obj
[property
]);
2543 jasmine
.StringPrettyPrinter
.prototype.append = function(value
) {
2544 this.string
+= value
;
2547 var prototype = jasmine
.PrettyPrinter
.prototype,
2548 superFormat
= prototype.format
;
2550 prototype.format = function(value
) {
2551 var className
, superclass
;
2554 className
= value
.$className
;
2556 if (className
!== undefined) {
2557 // support for pretty printing instances of Ext classes
2560 // support for anonymous classes - Ext.define(null, ...)
2561 // loop up the inheritance chain to find nearest non-anonymous ancestor
2562 superclass
= value
.superclass
;
2563 while (superclass
&& !superclass
.$className
) {
2564 superclass
= superclass
.superclass
;
2567 className
= superclass
.$className
;
2570 this.emitScalar(className
+ '#' + (value
.id
|| (value
.getId
&& value
.getId())));
2575 superFormat
.call(this, value
);
2577 })();jasmine
.Queue = function(env
) {
2580 // parallel to blocks. each true value in this array means the block will
2581 // get executed even if we abort
2584 this.running
= false;
2590 jasmine
.Queue
.prototype.addBefore = function(block
, ensure
) {
2591 if (ensure
=== jasmine
.undefined) {
2595 this.blocks
.unshift(block
);
2596 this.ensured
.unshift(ensure
);
2599 jasmine
.Queue
.prototype.add = function(block
, ensure
) {
2600 if (ensure
=== jasmine
.undefined) {
2604 this.blocks
.push(block
);
2605 this.ensured
.push(ensure
);
2608 jasmine
.Queue
.prototype.insertNext = function(block
, ensure
) {
2609 if (ensure
=== jasmine
.undefined) {
2613 this.ensured
.splice((this.index
+ this.offset
+ 1), 0, ensure
);
2614 this.blocks
.splice((this.index
+ this.offset
+ 1), 0, block
);
2618 jasmine
.Queue
.prototype.start = function(onComplete
) {
2619 this.running
= true;
2620 this.onComplete
= onComplete
;
2624 jasmine
.Queue
.prototype.isRunning = function() {
2625 return this.running
;
2628 jasmine
.Queue
.LOOP_DONT_RECURSE
= true;
2630 jasmine
.Queue
.prototype.next_ = function() {
2637 if (self
.index
< self
.blocks
.length
&& !(this.abort
&& !this.ensured
[self
.index
])) {
2638 var calledSynchronously
= true;
2639 var completedSynchronously
= false;
2641 var onComplete = function () {
2642 if (jasmine
.Queue
.LOOP_DONT_RECURSE
&& calledSynchronously
) {
2643 completedSynchronously
= true;
2647 if (self
.blocks
[self
.index
].abort
) {
2654 var now
= new Date().getTime();
2655 if (self
.env
.updateInterval
&& now
- self
.env
.lastUpdate
> self
.env
.updateInterval
) {
2656 self
.env
.lastUpdate
= now
;
2657 self
.env
.setTimeout(function() {
2661 if (jasmine
.Queue
.LOOP_DONT_RECURSE
&& completedSynchronously
) {
2668 self
.blocks
[self
.index
].execute(onComplete
);
2670 calledSynchronously
= false;
2671 if (completedSynchronously
) {
2676 self
.running
= false;
2677 if (self
.onComplete
) {
2684 jasmine
.Queue
.prototype.results = function() {
2685 var results
= new jasmine
.NestedResults();
2686 for (var i
= 0; i
< this.blocks
.length
; i
++) {
2687 if (this.blocks
[i
].results
) {
2688 results
.addResult(this.blocks
[i
].results());
2695 jasmine
.Queue
.prototype.next_ = function() {
2702 if (self
.index
< self
.blocks
.length
) {
2703 var calledSynchronously
= true;
2704 var completedSynchronously
= false;
2706 var onComplete = function() {
2707 if (jasmine
.Queue
.LOOP_DONT_RECURSE
&& calledSynchronously
) {
2708 completedSynchronously
= true;
2712 if (self
.blocks
[self
.index
].abort
) {
2719 var now
= new Date().getTime();
2721 if (self
.env
.updateInterval
&& now
- self
.env
.lastUpdate
> self
.env
.updateInterval
) {
2722 self
.env
.lastUpdate
= now
;
2723 self
.env
.setTimeout(function() {
2728 if (jasmine
.Queue
.LOOP_DONT_RECURSE
&& completedSynchronously
) {
2737 if (!this.abort
|| this.ensured
[self
.index
]) {
2738 self
.blocks
[self
.index
].execute(onComplete
);
2744 calledSynchronously
= false;
2746 if (completedSynchronously
) {
2751 self
.running
= false;
2753 if (self
.onComplete
) {
2762 jasmine
.Queue
.prototype.finish = function() {
2767 // Block functions are closures that keep a lot of test objects retained.
2768 // We don't want all this cruft hanging around when the queue is finished.
2769 for (i
= 0, len
= blocks
.length
; i
< len
; i
++) {
2774 if (block
.latchFunction
) {
2775 block
.latchFunction
= null;
2778 if (block
.timeout
) {
2779 block
.timeout
= null;
2789 * @param {jasmine.Env} env
2791 jasmine
.Runner = function(env
) {
2794 self
.queue
= new jasmine
.Queue(env
);
2800 jasmine
.Runner
.prototype.execute = function() {
2802 if (self
.env
.reporter
.reportRunnerStarting
) {
2803 self
.env
.reporter
.reportRunnerStarting(this);
2805 self
.queue
.start(function () {
2806 self
.finishCallback();
2810 jasmine
.Runner
.prototype.beforeEach = function(beforeEachFunction
) {
2811 beforeEachFunction
.typeName
= 'beforeEach';
2812 this.before_
.splice(0,0,beforeEachFunction
);
2815 jasmine
.Runner
.prototype.afterEach = function(afterEachFunction
) {
2816 afterEachFunction
.typeName
= 'afterEach';
2817 this.after_
.splice(0,0,afterEachFunction
);
2821 jasmine
.Runner
.prototype.finishCallback = function() {
2822 this.env
.reporter
.reportRunnerResults(this);
2825 jasmine
.Runner
.prototype.addSuite = function(suite
) {
2826 this.suites_
.push(suite
);
2829 jasmine
.Runner
.prototype.add = function(block
) {
2830 if (block
instanceof jasmine
.Suite
) {
2831 this.addSuite(block
);
2833 this.queue
.add(block
);
2836 jasmine
.Runner
.prototype.specs = function () {
2837 var suites
= this.suites();
2839 for (var i
= 0; i
< suites
.length
; i
++) {
2840 specs
.push
.apply(specs
, suites
[i
].specs());
2845 jasmine
.Runner
.prototype.suites = function() {
2846 return this.suites_
;
2849 jasmine
.Runner
.prototype.topLevelSuites = function() {
2850 var topLevelSuites
= [];
2851 for (var i
= 0; i
< this.suites_
.length
; i
++) {
2852 if (!this.suites_
[i
].parentSuite
) {
2853 topLevelSuites
.push(this.suites_
[i
]);
2856 return topLevelSuites
;
2859 jasmine
.Runner
.prototype.results = function() {
2860 return this.queue
.results();
2862 jasmine
.Runner
.prototype.filter = function(suiteIds
, specIds
) {
2863 // convert [1, 2] into { 1: true, 2: true }
2865 if (typeof suiteIds
.length
== 'number') {
2866 suiteIds
= jasmine
.toMap(suiteIds
);
2868 if (typeof specIds
.length
== 'number') {
2869 specIds
= jasmine
.toMap(specIds
);
2872 var specs
= jasmine
.getByIds(this.specs(), specIds
),
2873 suites
= jasmine
.getByIds(this.suites(), suiteIds
),
2877 length
= specs
.length
;
2878 for (i
= 0; i
< length
; i
++) {
2879 suite
= specs
[i
].getRootSuite();
2880 if (jasmine
.array
.indexOf(blocks
, suite
) === -1) {
2881 suite
.filter(suiteIds
, specIds
);
2886 length
= suites
.length
;
2887 for (i
= 0; i
< length
; i
++) {
2888 suite
= suites
[i
].getRootSuite();
2889 if (jasmine
.array
.indexOf(blocks
, suite
) === -1) {
2890 suite
.filter(suiteIds
, specIds
);
2895 if (blocks
.length
) {
2896 this.queue
.blocks
= this.queue
.ensured
= blocks
;
2898 // Kill the specs that are never going to run
2899 var runningSuites
= {};
2901 for (i
= 0, length
= blocks
.length
; i
< length
; i
++) {
2902 suites
= blocks
[i
].allSuites();
2904 for (var j
= 0, jlen
= suites
.length
; j
< jlen
; j
++) {
2907 runningSuites
[suite
.id
] = true;
2911 suites
= this.suites();
2913 for (i
= suites
.length
- 1; i
>= 0; i
--) {
2916 if (!runningSuites
[suite
.id
]) {
2921 // If suiteIds or specIds are provided, that means the user wanted to run
2922 // only the specs or suites specified. If these can't be found, most probably
2923 // that means there was a typo in a spec name, or recent changes to the spec code
2924 // changed the hash and it's no longer valid. Either way that is something that
2925 // happens only when debugging and is going to be corrected soon.
2926 // So instead of defaulting to run the whole nine yars, just bail out.
2927 else if (specs
.length
|| suites
.length
) {
2928 this.queue
.blocks
= [];
2929 Ext
.log
.error('No suites or specs found!');
2932 blocks
= this.queue
.blocks
;
2935 this.env
.totalSpecs
= 0;
2936 for (i
= 0; i
< blocks
.length
; ++i
) {
2937 this.env
.totalSpecs
+= blocks
[i
].totalSpecs
;
2939 this.env
.remainingSpecs
= this.env
.totalSpecs
;
2941 // We also no longer need hashes
2942 jasmine
.hashes
= null;
2947 * Internal representation of a Jasmine specification, or test.
2950 * @param {jasmine.Env} env
2951 * @param {jasmine.Suite} suite
2952 * @param {String} description
2954 jasmine
.Spec = function(env
, suite
, description
) {
2956 throw new Error('jasmine.Env() required');
2959 throw new Error('jasmine.Suite() required');
2962 spec
.id
= env
.nextSpecId
? env
.nextSpecId() : null;
2965 spec
.description
= description
;
2966 spec
.queue
= new jasmine
.Queue(env
);
2968 spec
.afterCallbacks
= [];
2971 spec
.results_
= new jasmine
.NestedResults();
2972 spec
.results_
.description
= description
;
2973 spec
.matchersClass
= null;
2976 jasmine
.Spec
.prototype.getFullName = function() {
2977 return this.suite
.getFullName() + ' ' + this.description
+ '.';
2981 jasmine
.Spec
.prototype.results = function() {
2982 return this.results_
;
2986 * All parameters are pretty-printed and concatenated together, then written to the spec's output.
2988 * Be careful not to leave calls to <code>jasmine.log</code> in production code.
2990 jasmine
.Spec
.prototype.log = function() {
2991 return this.results_
.log(arguments
);
2994 jasmine
.Spec
.prototype.runs = function (func
) {
2995 var block
= new jasmine
.Block(this.env
, func
, this);
2996 this.addToQueue(block
);
3000 jasmine
.Spec
.prototype.addToQueue = function (block
) {
3001 if (this.queue
.isRunning()) {
3002 this.queue
.insertNext(block
);
3004 this.queue
.add(block
);
3009 * @param {jasmine.ExpectationResult} result
3011 jasmine
.Spec
.prototype.addMatcherResult = function(result
) {
3012 this.results_
.addResult(result
);
3015 jasmine
.Spec
.prototype.expect = function(actual
) {
3016 var positive
= new (this.getMatchersClass_())(this.env
, actual
, this);
3017 positive
.not
= new (this.getMatchersClass_())(this.env
, actual
, this, true);
3022 * Waits a fixed time period before moving to the next block.
3024 * @deprecated Use waitsFor() instead
3025 * @param {Number} timeout milliseconds to wait
3027 jasmine
.Spec
.prototype.waits = function(timeout
) {
3028 var waitsFunc
= new jasmine
.WaitsBlock(this.env
, timeout
, this);
3029 this.addToQueue(waitsFunc
);
3034 * Waits for the latchFunction to return true before proceeding to the next block.
3036 * @param {Function} latchFunction Function to execute
3037 * @param {String} optional_timeoutMessage Message to use if the condition is never met
3038 * @param {Number} optional_timeout Time to wait for condition to be met.
3039 * @param {Number} optional_timeout_increment Number of milliseconds to wait between invocations.
3041 jasmine
.Spec
.prototype.waitsFor = function(latchFunction
, optional_timeoutMessage
, optional_timeout
, optional_timeout_increment
) {
3042 var latchFunction_
= null;
3043 var optional_timeoutMessage_
= null;
3044 var optional_timeout_
= null;
3045 var optional_timeout_increment_
= null;
3046 var numberFound
= false;
3048 for (var i
= 0; i
< arguments
.length
; i
++) {
3049 var arg
= arguments
[i
];
3050 switch (typeof arg
) {
3052 latchFunction_
= arg
;
3055 optional_timeoutMessage_
= arg
;
3058 // SECOND number is the increment
3060 optional_timeout_increment_
= arg
;
3062 // FIRST number is the timeout
3064 optional_timeout_
= arg
;
3071 var waitsForFunc
= new jasmine
.WaitsForBlock(this.env
, optional_timeout_
, latchFunction_
, optional_timeoutMessage_
, optional_timeout_increment_
, this);
3072 this.addToQueue(waitsForFunc
);
3076 jasmine
.Spec
.prototype.fail = function (e
) {
3077 var expectationResult
= new jasmine
.ExpectationResult({
3079 message
: e
? jasmine
.util
.formatException(e
) : 'Exception',
3080 trace
: { stack
: e
.stack
}
3082 this.results_
.addResult(expectationResult
);
3085 jasmine
.Spec
.prototype.getMatchersClass_ = function() {
3086 return this.matchersClass
|| this.env
.matchersClass
;
3089 jasmine
.Spec
.prototype.addMatchers = function(matchersPrototype
) {
3090 var parent
= this.getMatchersClass_();
3091 var newMatchersClass = function() {
3092 parent
.apply(this, arguments
);
3094 jasmine
.util
.inherit(newMatchersClass
, parent
);
3095 jasmine
.Matchers
.wrapInto_(matchersPrototype
, newMatchersClass
);
3096 this.matchersClass
= newMatchersClass
;
3099 jasmine
.Spec
.prototype.finishCallback = function() {
3100 this.env
.reporter
.reportSpecResults(this);
3103 jasmine
.Spec
.prototype.finish = function(onComplete
) {
3104 this.removeAllSpies();
3105 this.finishCallback();
3110 this.finished
= true;
3111 this.env
= this.afterCallbacks_
= this.spies_
= this.spy
= this.matchersClass
= null;
3114 jasmine
.Spec
.prototype.after = function(doAfter
) {
3115 if (this.queue
.isRunning()) {
3116 this.queue
.add(new jasmine
.Block(this.env
, doAfter
, this), true);
3118 this.afterCallbacks
.unshift(doAfter
);
3122 jasmine
.Spec
.prototype.execute = function(onComplete
) {
3124 if (!spec
.env
.specFilter(spec
)) {
3125 spec
.results_
.skipped
= true;
3126 spec
.finish(onComplete
);
3130 this.env
.reporter
.reportSpecStarting(this);
3132 spec
.env
.currentSpec
= spec
;
3134 spec
.addBeforesAndAftersToQueue();
3136 spec
.queue
.start(function () {
3137 spec
.finish(onComplete
);
3141 jasmine
.Spec
.prototype.addBeforesAndAftersToQueue = function() {
3142 var runner
= this.env
.currentRunner();
3145 for (var suite
= this.suite
; suite
; suite
= suite
.parentSuite
) {
3146 for (i
= 0; i
< suite
.before_
.length
; i
++) {
3147 this.queue
.addBefore(new jasmine
.Block(this.env
, suite
.before_
[i
], this));
3150 for (i
= 0; i
< runner
.before_
.length
; i
++) {
3151 this.queue
.addBefore(new jasmine
.Block(this.env
, runner
.before_
[i
], this));
3153 for (i
= 0; i
< this.afterCallbacks
.length
; i
++) {
3154 this.queue
.add(new jasmine
.Block(this.env
, this.afterCallbacks
[i
], this), true);
3156 for (suite
= this.suite
; suite
; suite
= suite
.parentSuite
) {
3157 for (i
= 0; i
< suite
.after_
.length
; i
++) {
3158 this.queue
.add(new jasmine
.Block(this.env
, suite
.after_
[i
], this), true);
3161 for (i
= 0; i
< runner
.after_
.length
; i
++) {
3162 this.queue
.add(new jasmine
.Block(this.env
, runner
.after_
[i
], this), true);
3166 jasmine
.Spec
.prototype.explodes = function() {
3167 throw 'explodes function should not have been called';
3170 jasmine
.Spec
.prototype.spyOn = function(obj
, methodName
, ignoreMethodDoesntExist
) {
3171 if (obj
== jasmine
.undefined) {
3172 throw "spyOn could not find an object to spy upon for " + methodName
+ "()";
3175 if (!ignoreMethodDoesntExist
&& obj
[methodName
] === jasmine
.undefined) {
3176 throw methodName
+ '() method does not exist';
3179 if (!ignoreMethodDoesntExist
&& obj
[methodName
] && obj
[methodName
].isSpy
) {
3180 throw new Error(methodName
+ ' has already been spied upon');
3183 var spyObj
= jasmine
.createSpy(methodName
);
3185 this.spies_
.push(spyObj
);
3186 spyObj
.baseObj
= obj
;
3187 spyObj
.methodName
= methodName
;
3188 spyObj
.originalValue
= obj
[methodName
];
3190 obj
[methodName
] = spyObj
;
3195 jasmine
.Spec
.prototype.removeAllSpies = function() {
3196 for (var i
= 0; i
< this.spies_
.length
; i
++) {
3197 var spy
= this.spies_
[i
];
3198 spy
.baseObj
[spy
.methodName
] = spy
.originalValue
;
3204 var _Spec
= jasmine
.Spec
,
3205 proto
= _Spec
.prototype,
3206 allowedGlobals
= {},
3207 allowedComponents
= {},
3210 // Any properties already in the window object when we are loading jasmine are ok
3211 for (prop
in window
) {
3212 allowedGlobals
[prop
] = true;
3215 // Old Firefox needs these
3216 allowedGlobals
.getInterface
=
3217 allowedGlobals
.loadFirebugConsole
=
3218 allowedGlobals
._createFirebugConsole
=
3219 allowedGlobals
.netscape
=
3220 allowedGlobals
.XPCSafeJSObjectWrapper
=
3221 allowedGlobals
.XPCNativeWrapper
=
3222 allowedGlobals
.Components
=
3223 allowedGlobals
._firebug
=
3224 // IE10+ F12 dev tools adds these properties when opened.
3225 allowedGlobals
.__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE
=
3226 allowedGlobals
.__BROWSERTOOLS_CONSOLE_BREAKMODE_FUNC
=
3227 allowedGlobals
.__BROWSERTOOLS_CONSOLE_SAFEFUNC
=
3228 // in IE8 jasmine's overrides of setTimeout/setInterval make them iterable
3229 allowedGlobals
.setTimeout
=
3230 allowedGlobals
.setInterval
=
3231 allowedGlobals
.clearTimeout
=
3232 allowedGlobals
.clearInterval
=
3233 // we're going to add the addGlobal function to the window object, so specs can call it
3234 allowedGlobals
.addGlobal
=
3235 allowedGlobals
.id
= true; // In Ext JS 4 Ext.get(window) adds an id property
3237 if (Ext
.toolkit
=== 'modern') {
3238 // Modern MessageBox owns a modal mask component
3239 allowedComponents
[Ext
.Msg
.id
] = true;
3240 allowedComponents
[Ext
.Msg
.getModal().id
] = true;
3243 // Ext.sparkline.Base puts this tooltip on its prototype
3244 allowedComponents
['sparklines-tooltip'] = true;
3247 // Ext.MessageBox and its children are going to be present in all tests.
3248 // The reason why we don't just add allow everything that is already present
3249 // in Ext.ComponentMgr collection is that sometimes components can be created
3250 // by mistake in spec definition. We want to catch these as well.
3252 var msgbox
= Ext
.MessageBox
,
3254 components
= Ext
.ComponentMgr
.getAll();
3256 for (var i
= 0; i
< components
.length
; i
++) {
3257 var cmp
= components
[i
];
3259 if (cmp
=== msgbox
|| (cmp
.up
&& cmp
.up('window') === msgbox
)) {
3260 allowedComponents
[cmp
.id
] = true;
3262 else if (!allowedComponents
[cmp
.id
]) {
3271 msg
: 'COMPONENTS EXIST BEFORE TEST SUITE START'
3275 Ext
.ComponentManager
.clearAll();
3278 window
.addGlobal = function(property
) {
3281 if (property
.charAt
) { // string
3282 allowedGlobals
[property
] = true;
3284 for (len
= property
.length
; len
--;) {
3285 allowedGlobals
[property
[len
]] = true;
3290 jasmine
.Spec = function () {
3291 _Spec
.apply(this, arguments
);
3292 this.fileName
= jasmine
.getCurrentScript();
3293 this.id
= jasmine
.hashString(this.getFullName(), this.suite
.id
);
3294 this.totalSpecs
= 1;
3297 jasmine
.Spec
.prototype = proto
;
3299 // Override: adds the error to the result
3300 proto
.fail = function (e
) {
3301 var expectationResult
= new jasmine
.ExpectationResult({
3303 message
: e
? jasmine
.util
.formatException(e
) : 'Exception'
3305 // Modification start
3306 if (e
instanceof Error
) {
3307 expectationResult
.error
= e
;
3310 this.results_
.addResult(expectationResult
);
3313 // Override: check for DOM and global variable leaks
3314 proto
.finishCallback = function() {
3315 if (!jasmine
.DISABLE_LEAK_CHECKS
) {
3316 this.checkDomLeak();
3317 this.checkGlobalsLeak();
3318 this.checkComponentLeak();
3321 if (Ext
.toolkit
=== 'classic') {
3322 this.checkLayoutSuspension();
3323 this.checkFocusSuspension();
3326 // TODO: this causes too many failures so is disabled for now.
3327 // clean up orphan elements and re-enable this at some point.
3328 // this.collectGarbage();
3330 Ext
.event
.publisher
.Gesture
.instance
.reset();
3332 this.env
.reporter
.reportSpecResults(this);
3334 // Once the results have been reported, we don't need to keep them anymore;
3335 // except when we're running under Cmd. Unlike local reporter, Cmd collects
3336 // results in batches per suite so we need to keep the results until the
3337 // collection is done. Cmd will then run the suite cleanup.
3339 this.cleanupResults();
3343 proto
.cleanupResults = function() {
3344 var results
= this.results();
3351 proto
.checkDomLeak = function() {
3352 var body
= document
.body
,
3353 children
= body
&& body
.childNodes
|| [],
3354 len
= children
.length
,
3360 for (; i
< len
; i
++) {
3361 child
= children
[i
];
3363 if (child
.nodeType
=== 3 || !child
.getAttribute('data-sticky')) {
3364 badIds
.push(child
.tagName
+ '#' + child
.id
);
3365 badNodes
.push(child
);
3369 for (i
= 0, len
= badNodes
.length
; i
< len
; i
++) {
3370 document
.body
.removeChild(badNodes
[i
]);
3373 if (badNodes
.length
) {
3374 ids
= badIds
.join(', ');
3379 msg
: 'CLEAN UP YOUR DOM LEAKS IN SPEC: ' + this.getFullName()
3382 this.fail('document.body contains childNodes after spec execution --> ' + this.getFullName());
3386 proto
.checkComponentLeak = function(spec
) {
3388 components
, i
, len
, cmp
;
3390 components
= Ext
.ComponentMgr
.getAll();
3392 for (i
= 0, len
= components
.length
; i
< len
; i
++) {
3393 cmp
= components
[i
];
3395 // Allow QuickTips by default, they're mostly harmless
3396 if (!allowedComponents
[cmp
.id
] &&
3397 !(cmp
.isQuickTip
|| (cmp
.up
&& cmp
.up('[isQuickTip]')))) {
3402 // We don't destroy the leaked components so that they could be examined,
3403 // just clearing them from ComponentManager cache is enough
3404 Ext
.ComponentMgr
.clearAll();
3410 msg
: 'CLEAN UP YOUR COMPONENT LEAKS IN SPEC: ' + this.getFullName()
3413 this.fail('Ext.ComponentMgr reports undestroyed components after spec execution');
3417 proto
.checkGlobalsLeak = function(spec
) {
3418 var property
, value
;
3420 for (property
in window
) {
3422 // IE throws error when trying to access window.localStorage
3423 value
= window
[property
];
3427 if (value
!== undefined && !allowedGlobals
[property
] &&
3428 (!value
|| // make sure we don't try to do a property lookup on a null value
3429 // old browsers (IE6 and opera 11) add element IDs as enumerable properties
3430 // of the window object, so make sure the global var is not a HTMLElement
3431 value
.nodeType
!== 1 &&
3432 // make sure it isn't a reference to a window object. This happens in
3433 // some browsers (e.g. IE6) when the document contains iframes. The
3434 // frames' window objects are referenced by id in the parent window object.
3435 !(value
.location
&& value
.document
))) {
3436 this.fail('Bad global variable: ' + property
+ ' = ' + value
);
3437 // add the bad global to allowed globals so that it only fails this one spec
3438 allowedGlobals
[property
] = true;
3443 proto
.checkLayoutSuspension = function(spec
) {
3444 var count
= Ext
.Component
.layoutSuspendCount
;
3446 this.fail('Spec completed with layouts suspended: count=' + count
);
3447 Ext
.Component
.layoutSuspendCount
= 0;
3451 proto
.checkFocusSuspension = function(spec
) {
3452 // If the ExtJS version supports focus suspension...
3453 if (Ext
.suspendFocus
) {
3454 var count
= Ext
.event
.publisher
.Focus
.instance
.suspendCount
;
3456 this.fail('Spec completed with focus suspended: count=' + count
);
3457 Ext
.event
.publisher
.Focus
.instance
.suspendCount
= 0;
3462 proto
.collectGarbage = function() {
3463 var ids
= Ext
.dom
.GarbageCollector
.collect();
3466 this.fail("Orphan Ext.dom.Element(s) detected: '" + ids
.join("', '") + "'");
3470 proto
.execute = function(onComplete
) {
3472 if (!spec
.env
.specFilter(spec
)) {
3473 spec
.results_
.skipped
= true;
3478 this.env
.reporter
.reportSpecStarting(this);
3480 if (spec
.isDisabled()) {
3481 spec
.results_
.skipped
= true;
3482 spec
.finish(onComplete
);
3486 spec
.env
.currentSpec
= spec
;
3488 spec
.addBeforesAndAftersToQueue();
3490 if (spec
.debugBlocks
&& jasmine
.getOptions().debug
=== true) {
3491 var blockIdx
= jasmine
.getOptions().block
;
3492 if (typeof blockIdx
!== 'undefined') {
3493 blockIdx
= parseInt(blockIdx
);
3494 var blocks
= this.queue
.blocks
,
3495 length
= blocks
.length
,
3499 for (; i
< length
; i
++) {
3501 if (i
=== blockIdx
) {
3502 block
.func
= jasmine
.generateDebuggableBlock(block
.func
);
3506 jasmine
.showDebugPrompt(function() {
3507 spec
.queue
.start(function () {
3508 spec
.finish(onComplete
);
3512 spec
.queue
.start(function () {
3513 spec
.finish(onComplete
);
3518 proto
.enabled
= true;
3520 proto
.isEnabled = function() {
3521 return this.enabled
;
3524 proto
.isDisabled = function() {
3525 return !this.enabled
;
3528 proto
.disable = function() {
3529 this.enabled
= false;
3531 // Release bound contexts and closures
3532 this.queue
.finish();
3537 proto
.enable = function() {
3538 this.enabled
= true;
3543 proto
.getRootSuite = function() {
3544 var suite
= this.suite
;
3546 while (suite
.parentSuite
) {
3547 suite
= suite
.parentSuite
;
3555 * Works just like waits() and waitsFor(), except waits for the next animationFrame
3557 function waitsForAnimation() {
3560 Ext
.Function
.requestAnimationFrame(function() {
3561 setTimeout(function() {
3566 waitsFor(function() {
3572 * Waits for the Spy to have been called before proceeding to the next block.
3574 * @param {Function} spy to wait for
3575 * @param {String} [timeoutMessage] Optional timeout message
3576 * @param {Number} [timeout] Optional timeout in ms
3578 function waitsForSpy(spy
, timeoutMessage
, timeout
) {
3579 var currentSpec
= jasmine
.getEnv().currentSpec
;
3581 timeoutMessage
= timeoutMessage
|| spy
.identity
+ ' to fire';
3582 timeout
= timeout
!= null ? timeout
: 1000;
3584 currentSpec
.waitsFor
.call(currentSpec
, function() { return !!spy
.callCount
}, timeoutMessage
, timeout
);
3587 var waitForSpy
= waitsForSpy
;
3590 * Internal representation of a Jasmine suite.
3593 * @param {jasmine.Env} env
3594 * @param {String} description
3595 * @param {Function} specDefinitions
3596 * @param {jasmine.Suite} parentSuite
3598 jasmine
.Suite = function(env
, description
, specDefinitions
, parentSuite
) {
3600 self
.id
= env
.nextSuiteId
? env
.nextSuiteId() : null;
3601 self
.description
= description
;
3602 self
.queue
= new jasmine
.Queue(env
);
3603 self
.parentSuite
= parentSuite
;
3607 self
.children_
= [];
3612 jasmine
.Suite
.prototype.getFullName = function() {
3613 var fullName
= this.description
;
3614 for (var parentSuite
= this.parentSuite
; parentSuite
; parentSuite
= parentSuite
.parentSuite
) {
3615 fullName
= parentSuite
.description
+ ' ' + fullName
;
3620 jasmine
.Suite
.prototype.finish = function(onComplete
) {
3621 this.env
.reporter
.reportSuiteResults(this);
3622 this.finished
= true;
3623 if (typeof(onComplete
) == 'function') {
3627 // MUST NOT null the children_ property because that is needed to
3628 // traverse the suite's child nodes upon expand and collapse.
3629 this.env
= this.before_
= this.after_
= null;
3630 this.suites_
= this.specs_
= null;
3633 jasmine
.Suite
.prototype.beforeEach = function(beforeEachFunction
) {
3634 beforeEachFunction
.typeName
= 'beforeEach';
3635 this.before_
.unshift(beforeEachFunction
);
3638 jasmine
.Suite
.prototype.afterEach = function(afterEachFunction
) {
3639 afterEachFunction
.typeName
= 'afterEach';
3640 this.after_
.unshift(afterEachFunction
);
3643 jasmine
.Suite
.prototype.results = function() {
3644 return this.queue
.results();
3647 jasmine
.Suite
.prototype.add = function(suiteOrSpec
) {
3648 this.children_
.push(suiteOrSpec
);
3649 if (suiteOrSpec
instanceof jasmine
.Suite
) {
3650 this.suites_
.push(suiteOrSpec
);
3651 this.env
.currentRunner().addSuite(suiteOrSpec
);
3653 this.specs_
.push(suiteOrSpec
);
3655 this.queue
.add(suiteOrSpec
);
3658 jasmine
.Suite
.prototype.specs = function() {
3662 jasmine
.Suite
.prototype.suites = function() {
3663 return this.suites_
;
3666 jasmine
.Suite
.prototype.children = function() {
3667 return this.children_
;
3670 jasmine
.Suite
.prototype.execute = function(onComplete
) {
3672 this.queue
.start(function () {
3673 self
.finish(onComplete
);
3677 var _Suite
= jasmine
.Suite
,
3678 proto
= _Suite
.prototype;
3680 jasmine
.Suite = function () {
3681 _Suite
.apply(this, arguments
);
3683 var parentSuite
= this.parentSuite
;
3684 this.totalSpecs
= 0;
3685 this.fileName
= jasmine
.getCurrentScript();
3686 this.id
= jasmine
.hashString(this.getFullName(), parentSuite
? parentSuite
.id
: 0);
3689 jasmine
.Suite
.prototype = proto
;
3691 proto
.execute = function(onComplete
) {
3693 self
.env
.reporter
.reportSuiteStarting(self
); // override
3695 if (self
.isDisabled()) {
3696 self
.results
= self
.forceSkippedResults
;
3697 self
.disableChildren();
3700 this.queue
.start(function () {
3701 self
.finish(onComplete
);
3705 proto
.enabled
= true;
3707 proto
.isEnabled = function() {
3708 return this.enabled
;
3711 proto
.isDisabled = function() {
3712 return !this.enabled
;
3715 proto
.adjustCounts = function (amount
) {
3716 for (var suite
= this; suite
; suite
= suite
.parentSuite
) {
3717 suite
.totalSpecs
+= amount
;
3721 proto
.disable = function() {
3722 this.enabled
= false;
3726 proto
.enable = function() {
3727 this.enabled
= true;
3731 proto
.forceSkippedResults = function() {
3732 var results
= this.queue
.results();
3733 results
.skipped
= true;
3738 proto
.disableChildren = function() {
3739 var children
= this.children(),
3740 length
= children
.length
,
3743 for (; i
< length
; i
++) {
3744 children
[i
].disable();
3750 proto
.filter = function (suiteIds
, specIds
) {
3751 if (!suiteIds
[this.id
]) {
3752 var specs
= this.specs(),
3753 suites
= this.suites(),
3754 spec
, i
, suite
, length
;
3756 length
= specs
.length
;
3758 for (i
= 0; i
< length
; i
++) {
3760 if (!specIds
[spec
.id
]) {
3761 jasmine
.array
.remove(this.queue
.blocks
, spec
);
3762 this.adjustCounts(-spec
.totalSpecs
);
3766 length
= suites
.length
;
3768 for (i
= 0; i
< length
; i
++) {
3770 suite
.filter(suiteIds
, specIds
);
3772 jasmine
.array
.remove(this.queue
.blocks
, suite
);
3773 this.adjustCounts(-suite
.totalSpecs
);
3777 if (this.queue
.blocks
.length
=== 0) {
3785 proto
.getRootSuite = function() {
3788 while (suite
.parentSuite
) {
3789 suite
= suite
.parentSuite
;
3795 proto
.add = function(suiteOrSpec
) {
3796 this.children_
.push(suiteOrSpec
);
3797 if (suiteOrSpec
instanceof jasmine
.Suite
) {
3798 this.suites_
.push(suiteOrSpec
);
3799 this.env
.currentRunner().addSuite(suiteOrSpec
);
3801 this.specs_
.push(suiteOrSpec
);
3803 this.queue
.add(suiteOrSpec
);
3805 for (var p
= this; p
; p
= p
.parentSuite
) {
3806 p
.totalSpecs
+= suiteOrSpec
.totalSpecs
;
3810 proto
.allSuites = function() {
3812 suites
, subSuites
, i
, len
;
3814 suites
= this.suites();
3816 for (i
= 0, len
= suites
.length
; i
< len
; i
++) {
3817 all
.push(suites
[i
]);
3819 subSuites
= suites
[i
].allSuites();
3821 if (subSuites
.length
) {
3822 all
.push(subSuites
);
3826 return all
.length
? Ext
.Array
.flatten(all
) : all
;
3829 proto
.cleanupResults = function() {
3830 var results
= this.results();
3837 jasmine
.WaitsBlock = function(env
, timeout
, spec
) {
3838 this.timeout
= timeout
;
3839 jasmine
.Block
.call(this, env
, null, spec
);
3842 jasmine
.util
.inherit(jasmine
.WaitsBlock
, jasmine
.Block
);
3844 jasmine
.WaitsBlock
.prototype.execute = function (onComplete
) {
3845 if (jasmine
.VERBOSE
) {
3846 this.env
.reporter
.log('>> Jasmine waiting for ' + this.timeout
+ ' ms...');
3848 this.env
.setTimeout(function () {
3853 * A block which waits for some condition to become true, with timeout.
3856 * @extends jasmine.Block
3857 * @param {jasmine.Env} env The Jasmine environment.
3858 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.
3859 * @param {Function} latchFunction A function which returns true when the desired condition has been met.
3860 * @param {String} message The message to display if the desired condition hasn't been met within the given time period.
3861 * @param {NUmber} timeout_increment Time in milliseconds to wait between invocations.
3862 * @param {jasmine.Spec} spec The Jasmine spec.
3864 jasmine
.WaitsForBlock = function(env
, timeout
, latchFunction
, message
, timeout_increment
, spec
) {
3865 this.timeout
= timeout
|| env
.defaultTimeoutInterval
;
3866 this.latchFunction
= latchFunction
;
3867 this.message
= message
;
3868 this.totalTimeSpentWaitingForLatch
= 0;
3869 this.timeout_increment
= timeout_increment
|| jasmine
.WaitsForBlock
.TIMEOUT_INCREMENT
;
3870 jasmine
.Block
.call(this, env
, null, spec
);
3872 jasmine
.util
.inherit(jasmine
.WaitsForBlock
, jasmine
.Block
);
3874 jasmine
.WaitsForBlock
.TIMEOUT_INCREMENT
= 10;
3876 jasmine
.WaitsForBlock
.prototype.execute = function(onComplete
) {
3877 if (jasmine
.VERBOSE
) {
3878 this.env
.reporter
.log('>> Jasmine waiting for ' + (this.message
|| 'something to happen'));
3880 var latchFunctionResult
;
3882 latchFunctionResult
= this.latchFunction
.call(this.spec
, this.timeout
, this.totalTimeSpentWaitingForLatch
);
3889 if (latchFunctionResult
) {
3891 } else if (this.totalTimeSpentWaitingForLatch
>= this.timeout
) {
3892 var message
= 'timed out after ' + this.timeout
+ ' msec waiting for ' + (this.message
|| 'something to happen');
3901 this.totalTimeSpentWaitingForLatch
+= this.timeout_increment
;
3903 this.env
.setTimeout(function() {
3904 self
.execute(onComplete
);
3905 }, this.timeout_increment
);
3908 // Mock setTimeout, clearTimeout
3909 // Contributed by Pivotal Computer Systems, www.pivotalsf.com
3911 jasmine
.FakeTimer = function() {
3915 self
.setTimeout = function(funcToCall
, millis
) {
3916 self
.timeoutsMade
++;
3917 self
.scheduleFunction(self
.timeoutsMade
, funcToCall
, millis
, false);
3918 return self
.timeoutsMade
;
3921 self
.setInterval = function(funcToCall
, millis
) {
3922 self
.timeoutsMade
++;
3923 self
.scheduleFunction(self
.timeoutsMade
, funcToCall
, millis
, true);
3924 return self
.timeoutsMade
;
3927 self
.clearTimeout = function(timeoutKey
) {
3928 self
.scheduledFunctions
[timeoutKey
] = jasmine
.undefined;
3931 self
.clearInterval = function(timeoutKey
) {
3932 self
.scheduledFunctions
[timeoutKey
] = jasmine
.undefined;
3937 jasmine
.FakeTimer
.prototype.reset = function() {
3938 this.timeoutsMade
= 0;
3939 this.scheduledFunctions
= {};
3943 jasmine
.FakeTimer
.prototype.tick = function(millis
) {
3944 var oldMillis
= this.nowMillis
;
3945 var newMillis
= oldMillis
+ millis
;
3946 this.runFunctionsWithinRange(oldMillis
, newMillis
);
3947 this.nowMillis
= newMillis
;
3950 jasmine
.FakeTimer
.prototype.runFunctionsWithinRange = function(oldMillis
, nowMillis
) {
3952 var funcsToRun
= [];
3953 for (var timeoutKey
in this.scheduledFunctions
) {
3954 scheduledFunc
= this.scheduledFunctions
[timeoutKey
];
3955 if (scheduledFunc
!= jasmine
.undefined &&
3956 scheduledFunc
.runAtMillis
>= oldMillis
&&
3957 scheduledFunc
.runAtMillis
<= nowMillis
) {
3958 funcsToRun
.push(scheduledFunc
);
3959 this.scheduledFunctions
[timeoutKey
] = jasmine
.undefined;
3963 if (funcsToRun
.length
> 0) {
3964 funcsToRun
.sort(function(a
, b
) {
3965 return a
.runAtMillis
- b
.runAtMillis
;
3967 for (var i
= 0; i
< funcsToRun
.length
; ++i
) {
3969 var funcToRun
= funcsToRun
[i
];
3970 this.nowMillis
= funcToRun
.runAtMillis
;
3971 funcToRun
.funcToCall();
3972 if (funcToRun
.recurring
) {
3973 this.scheduleFunction(funcToRun
.timeoutKey
,
3974 funcToRun
.funcToCall
,
3981 this.runFunctionsWithinRange(oldMillis
, nowMillis
);
3985 jasmine
.FakeTimer
.prototype.scheduleFunction = function(timeoutKey
, funcToCall
, millis
, recurring
) {
3986 this.scheduledFunctions
[timeoutKey
] = {
3987 runAtMillis
: this.nowMillis
+ millis
,
3988 funcToCall
: funcToCall
,
3989 recurring
: recurring
,
3990 timeoutKey
: timeoutKey
,
3999 defaultFakeTimer
: new jasmine
.FakeTimer(),
4002 jasmine
.Clock
.assertInstalled();
4003 jasmine
.Clock
.defaultFakeTimer
.reset();
4006 tick: function(millis
) {
4007 jasmine
.Clock
.assertInstalled();
4008 jasmine
.Clock
.defaultFakeTimer
.tick(millis
);
4011 runFunctionsWithinRange: function(oldMillis
, nowMillis
) {
4012 jasmine
.Clock
.defaultFakeTimer
.runFunctionsWithinRange(oldMillis
, nowMillis
);
4015 scheduleFunction: function(timeoutKey
, funcToCall
, millis
, recurring
) {
4016 jasmine
.Clock
.defaultFakeTimer
.scheduleFunction(timeoutKey
, funcToCall
, millis
, recurring
);
4019 useMock: function() {
4020 if (!jasmine
.Clock
.isInstalled()) {
4021 var spec
= jasmine
.getEnv().currentSpec
;
4022 spec
.after(jasmine
.Clock
.uninstallMock
);
4024 jasmine
.Clock
.installMock();
4028 installMock: function() {
4029 jasmine
.Clock
.installed
= jasmine
.Clock
.defaultFakeTimer
;
4032 uninstallMock: function() {
4033 jasmine
.Clock
.assertInstalled();
4034 jasmine
.Clock
.installed
= jasmine
.Clock
.real
;
4038 setTimeout
: jasmine
.getGlobal().setTimeout
,
4039 clearTimeout
: jasmine
.getGlobal().clearTimeout
,
4040 setInterval
: jasmine
.getGlobal().setInterval
,
4041 clearInterval
: jasmine
.getGlobal().clearInterval
4044 assertInstalled: function() {
4045 if (!jasmine
.Clock
.isInstalled()) {
4046 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");
4050 isInstalled: function() {
4051 return jasmine
.Clock
.installed
== jasmine
.Clock
.defaultFakeTimer
;
4056 jasmine
.Clock
.installed
= jasmine
.Clock
.real
;
4058 //else for IE support
4059 jasmine
.getGlobal().setTimeout = function(funcToCall
, millis
) {
4060 if (jasmine
.Clock
.installed
.setTimeout
.apply
) {
4061 return jasmine
.Clock
.installed
.setTimeout
.apply(this, arguments
);
4063 return jasmine
.Clock
.installed
.setTimeout(funcToCall
, millis
);
4067 jasmine
.getGlobal().setInterval = function(funcToCall
, millis
) {
4068 if (jasmine
.Clock
.installed
.setInterval
.apply
) {
4069 return jasmine
.Clock
.installed
.setInterval
.apply(this, arguments
);
4071 return jasmine
.Clock
.installed
.setInterval(funcToCall
, millis
);
4075 jasmine
.getGlobal().clearTimeout = function(timeoutKey
) {
4076 if (jasmine
.Clock
.installed
.clearTimeout
.apply
) {
4077 return jasmine
.Clock
.installed
.clearTimeout
.apply(this, arguments
);
4079 return jasmine
.Clock
.installed
.clearTimeout(timeoutKey
);
4083 jasmine
.getGlobal().clearInterval = function(timeoutKey
) {
4084 if (jasmine
.Clock
.installed
.clearTimeout
.apply
) {
4085 return jasmine
.Clock
.installed
.clearInterval
.apply(this, arguments
);
4087 return jasmine
.Clock
.installed
.clearInterval(timeoutKey
);
4091 jasmine
.mouseToPointerMap
= {
4092 mousedown
: 'pointerdown',
4093 mousemove
: 'pointermove',
4094 mouseup
: 'pointerup',
4095 mouseover
: 'pointerover',
4096 mouseout
: 'pointerout',
4097 mouseenter
: 'pointerenter',
4098 mouseleave
: 'pointerleave'
4101 jasmine
.pointerEventsMap
= Ext
.supports
.MSPointerEvents
&& !Ext
.supports
.PointerEvents
? {
4102 // translation map for IE10
4103 pointerdown
: 'MSPointerDown',
4104 pointermove
: 'MSPointerMove',
4105 pointerup
: 'MSPointerUp',
4106 pointerover
: 'MSPointerOver',
4107 pointerout
: 'MSPointerOut',
4108 // IE10 does not have pointer events for enter/leave
4109 pointerenter
: 'mouseenter',
4110 pointerleave
: 'mouseleave'
4114 * Utility function to fire a fake mouse event to a given target element
4116 jasmine
.fireMouseEvent = function (target
, type
, x
, y
, button
, shiftKey
, ctrlKey
, altKey
) {
4117 var e
, doc
, docEl
, body
, ret
, pointerEventType
;
4119 target
= Ext
.getDom(target
&& target
.isComponent
? target
.el
: target
);
4121 throw 'Cannot fire mouse event on null element';
4123 doc
= target
.ownerDocument
|| document
;
4127 if (Ext
.isIE9m
&& doc
.createEventObject
){ // IE event model
4128 e
= doc
.createEventObject();
4129 docEl
= doc
.documentElement
;
4131 x
= x
+ (docEl
&& docEl
.clientLeft
|| 0) + (body
&& body
.clientLeft
|| 0);
4132 y
= y
+ (docEl
&& docEl
.clientTop
|| 0) + (body
&& body
.clientLeft
|| 0);
4140 button
: button
|| 1,
4141 shiftKey
: !!shiftKey
,
4145 if (type
=== 'click') {
4146 target
.fireEvent('onmousedown', e
);
4147 target
.fireEvent('onmouseup', e
);
4148 } else if (type
=== 'dblclick') {
4149 jasmine
.fireMouseEvent(target
, 'click', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4150 target
.fireEvent('onmousedown', e
);
4151 target
.fireEvent('onmouseup', e
);
4153 ret
= target
.fireEvent('on' + type
, e
);
4155 if (Ext
.supports
.PointerEvents
|| Ext
.supports
.MSPointerEvents
) {
4156 // In IE10 and higher the framework translates mouse event listeners to pointer
4157 // event listeners by default. This means that if we fire only mouse events, our
4158 // pointer event listeners will not be fired. To fix this, we have to emulate
4159 // what the browser does when the mouse is clicked or screen is touched - fire
4160 // a pointer event followed by a compatibility mouse event.
4161 // see http://www.w3.org/TR/pointerevents/#dfn-compatibility-mouse-events
4162 if (type
=== 'click') {
4163 // In IE10+ the framework translates click to tap, which means we must
4164 // fire the events from which tap is sythesized (pointerdown/pointerup)
4165 // if we want our listeners to run.
4166 jasmine
.firePointerEvent(target
, 'pointerdown', 1, x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4167 jasmine
.doFireMouseEvent(target
, 'mousedown', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4168 jasmine
.firePointerEvent(target
, 'pointerup', 1, x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4169 jasmine
.doFireMouseEvent(target
, 'mouseup', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4170 } else if (type
=== 'dblclick') {
4171 // click (which triggers its own (pointerdown/mousdown/pointerup/mouseup
4172 // sequence) followed by a second pointerdown/mousedown/pointerup/mouseup
4173 // sequence always precedes dblclick
4174 jasmine
.fireMouseEvent(target
, 'click', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4175 jasmine
.firePointerEvent(target
, 'pointerdown', 1, x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4176 jasmine
.doFireMouseEvent(target
, 'mousedown', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4177 jasmine
.firePointerEvent(target
, 'pointerup', 1, x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4178 jasmine
.doFireMouseEvent(target
, 'mouseup', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4180 // plain old mouse event (mousedown, mousemove, etc.) - fire the corresponding
4181 // pointer event before dispatching the mouse event
4182 pointerEventType
= jasmine
.mouseToPointerMap
[type
];
4183 if (pointerEventType
) {
4184 jasmine
.firePointerEvent(target
, pointerEventType
, 1, x
, y
, button
);
4187 } else if (type
=== 'click') {
4188 // simulate a mousedown/mouseup sequence before firing a click event
4189 jasmine
.fireMouseEvent(target
, 'mousedown', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4190 jasmine
.fireMouseEvent(target
, 'mouseup', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4191 } else if (type
=== 'dblclick') {
4192 // click (which includes its own mousedown/mouseup sequence) followed by a second
4193 // mousedown/mouseup always precedes dblclick
4194 jasmine
.fireMouseEvent(target
, 'click', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4195 jasmine
.fireMouseEvent(target
, 'mousedown', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4196 jasmine
.fireMouseEvent(target
, 'mouseup', x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4199 ret
= jasmine
.doFireMouseEvent(target
, type
, x
, y
, button
, shiftKey
, ctrlKey
, altKey
);
4202 return (ret
=== false) ? ret
: e
;
4205 jasmine
.doFireMouseEvent = function(target
, type
, x
, y
, button
, shiftKey
, ctrlKey
, altKey
) {
4206 var doc
= target
.ownerDocument
|| document
,
4207 e
= doc
.createEvent("MouseEvents");
4209 e
.initMouseEvent(type
, true, true, doc
.defaultView
|| doc
.parentWindow
, 1, x
, y
, x
, y
, !!ctrlKey
, !!altKey
, !!shiftKey
, false, button
|| 0, null);
4210 return target
.dispatchEvent(e
);
4214 * Fires a pointer event. Since PointerEvents cannot yet be directly constructed,
4215 * we fake it by constructing a mouse event and setting its pointer id. This method
4216 * should typically be used when (Ext.supports.PointerEvents || Ext.supports.MSPointerEvents).
4217 * @param {String/Ext.Element/HTMLElement} target
4218 * @param {String} type The name of the event to fire
4219 * @param {Number} pointerId A unique id for the pointer, for more on pointerId see
4220 * http://www.w3.org/TR/pointerevents/
4221 * @param {Number} x The x coordinate
4222 * @param {Number} y The y coordinate
4223 * @param {Number} button
4224 * @return {Boolean} true if the event was successfully dispatched
4226 jasmine
.firePointerEvent = function(target
, type
, pointerId
, x
, y
, button
, shiftKey
, ctrlKey
, altKey
) {
4228 e
= doc
.createEvent("MouseEvents"),
4229 target
= Ext
.getDom(target
),
4233 throw 'Cannot fire pointer event on null element';
4236 type
= jasmine
.pointerEventsMap
[type
] || type
;
4242 doc
.defaultView
|| doc
.parentWindow
, // view
4248 !!ctrlKey
, // ctrlKey
4250 !!shiftKey
, // shiftKey
4252 button
|| 0, // button
4253 null // relatedTarget
4255 e
.pointerId
= pointerId
|| 1;
4256 e
.pointerType
= 'mouse';
4258 dispatched
= target
.dispatchEvent(e
);
4260 return (dispatched
=== false) ? dispatched
: e
;
4263 jasmine
.createTouchList = function(touchList
, target
) {
4266 ln
= touchList
.length
,
4270 for (; i
< ln
; i
++) {
4271 touchCfg
= touchList
[i
];
4272 touches
.push(doc
.createTouch(
4273 doc
.defaultView
|| doc
.parentWindow
,
4274 touchCfg
.target
|| target
,
4275 // use 1 as the default ID, so that tests that are only concerned with a single
4276 // touch event don't need to worry about providing an ID
4277 touchCfg
.identifier
|| 1,
4280 touchCfg
.screenX
|| touchCfg
.pageX
, // use pageX/Y as the default for screenXY
4281 touchCfg
.screenY
|| touchCfg
.pageY
4285 return doc
.createTouchList
.apply(doc
, touches
);
4289 * Utility for emulating a touch event. This method should typically only be used when
4290 * Ext.supports.TouchEvents. Recommended reading for understanding how touch events work:
4291 * http://www.w3.org/TR/touch-events/
4292 * @param {String/Ext.Element/HTMLElement} target
4293 * @param {String} type The name of the event to fire
4294 * @param {Object[]} touches An array of config objects for constructing the event object's
4295 * "touches". The config objects conform to the following interface:
4296 * http://www.w3.org/TR/touch-events/#idl-def-Touch The only required properties
4297 * are pageX and pageY. this method provides defaults for the others.
4298 * @param {Object[]} changedTouches An array of config objects for constructing the event
4299 * object's "changedTouches" (defaults to the same value as the `touches` param)
4300 * @param {Object[]} targetTouches An array of config objects for constructing the event
4301 * object's "targetTouches" (defaults to the same value as the `touches` param)
4302 * @param {Number} scale
4303 * @param {Number} rotation
4304 * @return {Boolean} true if the event was successfully dispatched
4306 jasmine
.fireTouchEvent = function(target
, type
, touches
, changedTouches
, targetTouches
) {
4308 // Couldn't figure out how to set touches, changedTouches targetTouches on a "real"
4309 // TouchEvent, initTouchEvent seems to ignore the parameters documented here:
4310 // https://developer.apple.com/library/safari/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.htmlTouchLists
4311 // Apparently directly assigning to e.touches after creating a TouchEvent doesn't
4312 // work either so the best we can do is just make a CustomEvent and fake it.
4313 e
= new CustomEvent(type
, {
4317 target
= Ext
.getDom(target
),
4321 throw 'Cannot fire touch event on null element';
4326 touches
: jasmine
.createTouchList(touches
, target
),
4327 changedTouches
: jasmine
.createTouchList(changedTouches
? changedTouches
: touches
, target
),
4328 targetTouches
: jasmine
.createTouchList(targetTouches
? targetTouches
: touches
, target
)
4331 dispatched
= target
.dispatchEvent(e
);
4333 return (dispatched
=== false) ? dispatched
: e
;
4337 * Utility function to fire a fake key event to a given target element
4339 jasmine
.fireKeyEvent = function(target
, type
, key
, shiftKey
, ctrlKey
, altKey
) {
4342 target
= Ext
.getDom(target
);
4344 throw 'Cannot fire key event on null element';
4346 doc
= target
.ownerDocument
|| document
;
4347 if (Ext
.isIE9m
&& doc
.createEventObject
) { //IE event model
4348 e
= doc
.createEventObject();
4353 shiftKey
: !!shiftKey
,
4357 return target
.fireEvent('on' + type
, e
);
4359 e
= doc
.createEvent("Events");
4360 e
.initEvent(type
, true, true);
4363 shiftKey
: !!shiftKey
,
4367 return target
.dispatchEvent(e
);
4371 // This implementation is pretty naïve but since it's not easy to simulate
4372 // real Tab key presses (if at all possible), it doesn't make sense to go
4373 // any further than this.
4374 jasmine
.simulateTabKey
= jasmine
.syncPressTabKey = function(from, forward
) {
4375 function getNextTabTarget(currentlyFocused
, forward
) {
4376 var selector
= 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]',
4377 body
= Ext
.getBody(),
4378 currentDom
, focusables
, node
, next
,
4379 len
, lastIdx
, currIdx
, i
, to
, step
;
4381 currentDom
= Ext
.getDom(currentlyFocused
);
4382 focusables
= body
.dom
.querySelectorAll(selector
);
4383 len
= focusables
.length
;
4384 lastIdx
= focusables
.length
- 1;
4385 currIdx
= Ext
.Array
.indexOf(focusables
, currentDom
);
4387 // If the currently focused element is not present in the list,
4388 // it must be the body itself. Just focus the first or last
4389 // tabbable element.
4415 // We're only interested in the elements that an user can *tab into*,
4416 // not all programmatically focusable elements. So we have to filter
4418 for (;; i
+= step
) {
4419 if ((step
> 0 && i
> to
) || (step
< 0 && i
< to
)) {
4423 node
= focusables
[i
];
4425 if (Ext
.fly(node
).isTabbable()) {
4431 return Ext
.get(next
|| body
);
4434 from = from.isComponent
? from.getFocusEl() : from;
4436 jasmine
.fireKeyEvent(from, 'keydown', 9, forward
);
4438 // Compute the next target *after* firing keydown;
4439 // a handler somewhere could have changed tabbability!
4440 // Not only that but the focused element could have changed
4441 // as well so we have to account for it.
4442 var to
= getNextTabTarget(document
.activeElement
|| from, forward
);
4448 jasmine
.fireKeyEvent(to
|| from, 'keyup', 9, forward
);
4453 jasmine
.simulateArrowKey
= jasmine
.syncPressArrowKey
=
4454 jasmine
.simulateKey
= jasmine
.syncPressKey = function(from, key
, options
) {
4455 var keyCode
= Ext
.event
.Event
[key
.toUpperCase()];
4457 if (keyCode
=== undefined) {
4458 throw 'Cannot fire undefined key event!';
4461 from = from.isComponent
? from.getFocusEl() : from;
4463 var target
= Ext
.getDom(from);
4466 throw 'Cannot fire arrow key event on null element';
4469 var shiftKey
, ctrlKey
, altKey
;
4472 shiftKey
= options
.shift
|| options
.shiftKey
;
4473 ctrlKey
= options
.ctrl
|| options
.ctrlKey
;
4474 altKey
= options
.alt
|| options
.altKey
;
4477 jasmine
.fireKeyEvent(target
, 'keydown', keyCode
, shiftKey
, ctrlKey
, altKey
);
4479 // IE8 blows up with "unspecified error" :(
4481 jasmine
.fireKeyEvent(target
, 'keyup', keyCode
, shiftKey
, ctrlKey
, altKey
);
4485 // In IE, focus events are asynchronous so we often have to wait
4486 // after attempting to focus something. Otherwise tests will fail.
4487 jasmine
.waitForFocus
= jasmine
.waitsForFocus = function(cmp
, desc
, timeout
) {
4488 var isComponent
= cmp
.isComponent
,
4489 dom
= isComponent
? cmp
.getFocusEl().dom
4490 : cmp
.isElement
? cmp
.dom
4492 id
= isComponent
? (cmp
.itemId
|| cmp
.id
) : dom
.id
;
4495 desc
= id
+ ' to focus';
4498 timeout
= timeout
|| 1000;
4502 return document
.activeElement
=== dom
;
4509 jasmine
.waitForBlur
= jasmine
.waitsForBlur = function(cmp
, desc
, timeout
) {
4510 var dom
= cmp
.isComponent
? cmp
.getFocusEl().dom
4511 : cmp
.isElement
? cmp
.dom
4515 desc
= dom
.id
+ ' to blur';
4518 timeout
= timeout
|| 1000;
4522 return document
.activeElement
!== dom
;
4529 // In IE (all of 'em), focus/blur events are asynchronous. To us it means
4530 // not only that we have to wait for the actual element to focus but
4531 // also for its container-injected focus handler to fire; and since
4532 // container focus handler may focus yet another element we have to yield
4533 // for *that* focus handler to fire, too. The third `waits` is to
4534 // accommodate for any repercussions caused by secondary focus handler,
4535 // and of course as a good luck charm.
4536 // Note that the timeout value is not important here because effectively
4537 // we just want to yield enough cycles to unwind all the async event handlers
4538 // before the test checks done in the specs, so we default to 1 ms.
4539 jasmine
.waitAWhile
= jasmine
.waitsAWhile = function(timeout
) {
4540 timeout
= timeout
!= null ? timeout
: 1;
4547 jasmine
.focusAndWait = function(cmp
, waitFor
) {
4548 // Apparently IE has yet another odd problem with focusing some elements;
4549 // if dom.focus() is called before the element is fully initialized, focusing
4550 // will fail and focus will jump to the document body. This happens with
4551 // text inputs at the very least, maybe with some others as well.
4552 // In IE9-10 we work around this issue by giving it a bit of time to finish
4553 // whatever initialization it was doing; in IE8 some harsher measures are
4554 // required, see Ext.dom.Element override.
4556 jasmine
.waitAWhile();
4563 jasmine
.waitForFocus(waitFor
|| cmp
);
4565 jasmine
.waitAWhile();
4568 jasmine
.blurAndWait = function(cmp
, waitFor
) {
4570 // Programmatic blur fails on IEs, so just focus the body element instead.
4572 Ext
.getBody().focus();
4579 jasmine
.waitForBlur(waitFor
|| cmp
);
4581 jasmine
.waitAWhile();
4584 jasmine
.pressTabKey
= jasmine
.asyncPressTabKey = function(from, forward
) {
4585 jasmine
.focusAndWait(from, from);
4588 jasmine
.simulateTabKey(from, forward
);
4591 jasmine
.waitAWhile();
4594 jasmine
.pressArrowKey
= jasmine
.asyncPressArrowKey
=
4595 jasmine
.pressKey
= jasmine
.asyncPressKey = function(from, key
, options
) {
4596 jasmine
.focusAndWait(from);
4599 jasmine
.simulateArrowKey(from, key
, options
);
4602 jasmine
.waitAWhile();
4605 // Can't add this one and below as simple matchers,
4606 // because there's async waiting involved
4607 jasmine
.expectFocused
= jasmine
.expectsFocused = function(want
, noWait
) {
4609 jasmine
.waitForFocus(want
);
4613 var have
= want
.isComponent
? Ext
.ComponentManager
.getActiveComponent()
4614 : want
.isElement
? Ext
.fly(document
.activeElement
)
4615 : document
.activeElement
4618 expect(have
).toBe(want
);
4622 jasmine
.expectTabIndex
= jasmine
.expectsTabIndex = function(wantIndex
, el
) {
4624 if (el
&& el
.isComponent
) {
4625 el
= el
.getFocusEl();
4628 var haveIndex
= el
.dom
.getAttribute('tabIndex');
4630 expect(haveIndex
- 0).toBe(wantIndex
);
4634 jasmine
.expectAriaAttr = function(cmp
, attr
, value
) {
4635 var target
= cmp
.isComponent
? cmp
.ariaEl
: Ext
.get(cmp
);
4637 expect(target
.dom
.getAttribute(attr
)).toBe(value
);
4640 jasmine
.expectNoAriaAttr = function(cmp
, attr
) {
4641 var target
= cmp
.isComponent
? cmp
.ariaEl
: Ext
.get(cmp
);
4643 expect(target
.dom
.hasAttribute(attr
)).toBe(false);
4647 This version is commented out because there is a bug in WebKit that prevents
4648 key events to be fired with both options and a valid keycode. When the bug gets fixed
4649 this method can be reintroduced. See https://bugs.webkit.org/show_bug.cgi?id=16735
4650 jasmine.fireKeyEvent = function(target, type, key, options) {
4653 options = options || {};
4654 target = Ext.getDom(target);
4655 if (document.createEventObject) { //IE event model
4656 e = document.createEventObject();
4663 for (prop in options) {
4664 if (options.hasOwnProperty(prop)) {
4665 e[prop] = options[prop];
4669 return target.fireEvent('on' + type, e);
4672 e = document.createEvent('KeyboardEvent');
4673 if (typeof e.initKeyboardEvent != 'undefined') {
4674 e.initKeyboardEvent(type, true, true, window,
4680 e.initKeyEvent(type, true, true, window,
4681 options.ctrlKey || false,
4682 options.altKey || false,
4683 options.shiftKey || false,
4684 options.metaKey || false, key, key);
4686 return target.dispatchEvent(e);
4695 * Class to act as a bridge between the MockAjax class and Ext.data.Request
4697 var MockAjaxManager
= {
4699 getXhrInstance
: null,
4702 * Pushes methods onto the Request prototype to make it easier to deal with
4704 addMethods: function(){
4705 var Connection
= Ext
.data
.Connection
,
4706 connectionProto
= Connection
.prototype,
4707 requestProto
= Ext
.data
.request
.Ajax
.prototype;
4709 Connection
.requestId
= 0;
4710 MockAjaxManager
.getXhrInstance
= requestProto
.getXhrInstance
;
4713 * Template method to create the AJAX request
4715 requestProto
.getXhrInstance = function() {
4716 return new MockAjax();
4720 * Method to simulate a request completing
4721 * @param {Object} response The response
4722 * @param {String} id (optional) The id of the completed request
4724 connectionProto
.mockComplete = function(response
, id
) {
4725 var request
= this.mockGetRequestXHR(id
);
4728 request
.xhr
.complete(response
);
4733 * Get a particular request
4734 * @param {String} id (optional) The id of the request
4736 connectionProto
.mockGetRequestXHR = function(id
) {
4740 request
= this.requests
[id
];
4742 // get the first one
4743 request
= this.mockGetAllRequests()[0];
4745 return request
? request
: null;
4749 * Gets all the requests from the Connection
4751 connectionProto
.mockGetAllRequests = function(){
4752 var requests
= this.requests
,
4757 for (id
in requests
) {
4758 if (requests
.hasOwnProperty(id
)) {
4759 out
.push(requests
[id
]);
4765 this.originalExtAjax
= Ext
.Ajax
;
4766 Ext
.Ajax
= new Connection({ autoAbort
: false });
4770 * Restore any changes made by addMethods
4772 removeMethods: function() {
4773 var proto
= Ext
.data
.Connection
.prototype;
4774 delete proto
.mockComplete
;
4775 delete proto
.mockGetRequestXHR
;
4776 Ext
.Ajax
= this.originalExtAjax
;
4778 Ext
.data
.request
.Ajax
.prototype.getXhrInstance
= MockAjaxManager
.getXhrInstance
;
4779 MockAjaxManager
.getXhrInstance
= null;
4784 * Simple Mock class to represent an XMLHttpRequest
4786 var MockAjax = function(){
4788 * Contains all request headers
4793 * Contains any options specified during sending
4795 this.ajaxOptions
= {};
4797 this.readyState
= 0;
4801 this.responseText
= this.responseXML
= null;
4805 * Contains a default response for any synchronous request.
4807 MockAjax
.prototype.syncDefaults
= {
4808 responseText
: 'data',
4812 responseHeaders
: {"Content-type": "application/json" }
4815 MockAjax
.prototype.readyChange = function() {
4816 if (this.onreadystatechange
) {
4817 this.onreadystatechange();
4822 * Simulate the XHR open method
4823 * @param {Object} method
4824 * @param {Object} url
4825 * @param {Object} async
4826 * @param {Object} username
4827 * @param {Object} password
4829 MockAjax
.prototype.open = function(method
, url
, async
, username
, password
){
4830 var options
= this.ajaxOptions
;
4831 options
.method
= method
;
4833 options
.async
= async
;
4834 options
.username
= username
;
4835 options
.password
= password
;
4836 this.readyState
= 1;
4841 * Simulate the XHR send method
4842 * @param {Object} data
4844 MockAjax
.prototype.send = function(data
){
4845 this.ajaxOptions
.data
= data
;
4846 this.readyState
= 2;
4847 // if it's a synchronous request, let's just assume it's already finished
4848 if (!this.ajaxOptions
.async
) {
4849 this.complete(this.syncDefaults
);
4856 * Simulate the XHR abort method
4858 MockAjax
.prototype.abort = function(){
4859 this.readyState
= 0;
4864 * Simulate the XHR setRequestHeader method
4865 * @param {Object} header
4866 * @param {Object} value
4868 MockAjax
.prototype.setRequestHeader = function(header
, value
){
4869 this.headers
[header
] = value
;
4873 * Simulate the XHR getAllResponseHeaders method
4875 MockAjax
.prototype.getAllResponseHeaders = function(){
4876 var headers
= this.responseHeaders
,
4880 for (header
in headers
) {
4881 if (headers
.hasOwnProperty(header
)) {
4882 lines
.push(header
+ ': ' + headers
[header
]);
4886 return lines
.join('\r\n');
4890 * Simulate the XHR getResponseHeader method
4891 * @param {Object} name
4893 MockAjax
.prototype.getResponseHeader = function(name
){
4894 return this.responseHeaders
[header
];
4898 * Simulate the XHR onreadystatechange method
4900 MockAjax
.prototype.onreadystatechange = function(){
4904 * Method for triggering a response completion
4906 MockAjax
.prototype.complete = function(response
){
4907 this.responseText
= response
.responseText
|| '';
4908 this.status
= response
.status
;
4909 this.statusText
= response
.statusText
;
4910 this.responseXML
= response
.responseXML
|| this.xmlDOM(response
.responseText
);
4911 this.responseHeaders
= response
.responseHeaders
|| {"Content-type": response
.contentType
|| "application/json" };
4912 this.readyState
= 4;
4917 * Converts string to XML DOM
4919 MockAjax
.prototype.xmlDOM = function(xml
) {
4920 // IE DOMParser support
4921 if (!window
.DOMParser
&& window
.ActiveXObject
) {
4922 doc
= new ActiveXObject('Microsoft.XMLDOM');
4923 doc
.async
= 'false';
4924 DOMParser = function() {};
4925 DOMParser
.prototype.parseFromString = function(xmlString
) {
4926 var doc
= new ActiveXObject('Microsoft.XMLDOM');
4927 doc
.async
= 'false';
4928 doc
.loadXML(xmlString
);
4933 if (xml
&& xml
.substr(0, 1) === '<') {
4935 return (new DOMParser()).parseFromString(xml
, "text/xml");