]> git.proxmox.com Git - extjs.git/blame - extjs/modern/modern/.sencha/test/jasmine.js
add extjs 6.0.1 sources
[extjs.git] / extjs / modern / modern / .sencha / test / jasmine.js
CommitLineData
6527f429
DM
1var isCommonJS = typeof window == "undefined" && typeof exports == "object";\r
2\r
3/**\r
4 * Top level namespace for Jasmine, a lightweight JavaScript BDD/spec/testing framework.\r
5 *\r
6 * @namespace\r
7 */\r
8var jasmine = {};\r
9if (isCommonJS) exports.jasmine = jasmine;\r
10/**\r
11 * @private\r
12 */\r
13jasmine.unimplementedMethod_ = function() {\r
14 throw new Error("unimplemented method");\r
15};\r
16\r
17/**\r
18 * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code> is just\r
19 * a plain old variable and may be redefined by somebody else.\r
20 *\r
21 * @private\r
22 */\r
23jasmine.undefined = jasmine.___undefined___;\r
24\r
25/**\r
26 * Show diagnostic messages in the console if set to true\r
27 *\r
28 */\r
29jasmine.VERBOSE = false;\r
30\r
31/**\r
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.\r
33 *\r
34 */\r
35jasmine.DEFAULT_UPDATE_INTERVAL = 250;\r
36\r
37/**\r
38 * Maximum levels of nesting that will be included when an object is pretty-printed\r
39 */\r
40jasmine.MAX_PRETTY_PRINT_DEPTH = 40;\r
41\r
42/**\r
43 * Default timeout interval in milliseconds for waitsFor() blocks.\r
44 */\r
45jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;\r
46\r
47/**\r
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.\r
49 * Set to false to let the exception bubble up in the browser.\r
50 *\r
51 */\r
52jasmine.CATCH_EXCEPTIONS = true;\r
53\r
54jasmine.getGlobal = function() {\r
55 function getGlobal() {\r
56 return this;\r
57 }\r
58\r
59 return getGlobal();\r
60};\r
61\r
62/**\r
63 * Allows for bound functions to be compared. Internal use only.\r
64 *\r
65 * @ignore\r
66 * @private\r
67 * @param base {Object} bound 'this' for the function\r
68 * @param name {Function} function to find\r
69 */\r
70jasmine.bindOriginal_ = function(base, name) {\r
71 var original = base[name];\r
72 if (original.apply) {\r
73 return function() {\r
74 return original.apply(base, arguments);\r
75 };\r
76 } else {\r
77 // IE support\r
78 return jasmine.getGlobal()[name];\r
79 }\r
80};\r
81\r
82jasmine.setTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'setTimeout');\r
83jasmine.clearTimeout = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearTimeout');\r
84jasmine.setInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'setInterval');\r
85jasmine.clearInterval = jasmine.bindOriginal_(jasmine.getGlobal(), 'clearInterval');\r
86\r
87jasmine.MessageResult = function(values) {\r
88 this.type = 'log';\r
89 this.values = values;\r
90 this.trace = new Error(); // todo: test better\r
91};\r
92\r
93jasmine.MessageResult.prototype.toString = function() {\r
94 var text = "";\r
95 for (var i = 0; i < this.values.length; i++) {\r
96 if (i > 0) text += " ";\r
97 if (jasmine.isString_(this.values[i])) {\r
98 text += this.values[i];\r
99 } else {\r
100 text += jasmine.pp(this.values[i]);\r
101 }\r
102 }\r
103 return text;\r
104};\r
105\r
106jasmine.ExpectationResult = function(params) {\r
107 this.type = 'expect';\r
108 this.matcherName = params.matcherName;\r
109 this.passed_ = params.passed;\r
110 this.expected = params.expected;\r
111 this.actual = params.actual;\r
112 this.message = this.passed_ ? 'Passed.' : params.message;\r
113\r
114 var trace = (params.trace || new Error(this.message));\r
115 this.trace = this.passed_ ? '' : trace + '';\r
116};\r
117\r
118jasmine.ExpectationResult.prototype.toString = function () {\r
119 return this.message;\r
120};\r
121\r
122jasmine.ExpectationResult.prototype.passed = function () {\r
123 return this.passed_;\r
124};\r
125\r
126/**\r
127 * Getter for the Jasmine environment. Ensures one gets created\r
128 */\r
129jasmine.getEnv = function() {\r
130 var env = jasmine.currentEnv_ = jasmine.currentEnv_ || new jasmine.Env();\r
131 return env;\r
132};\r
133\r
134/**\r
135 * @ignore\r
136 * @private\r
137 * @param value\r
138 * @returns {Boolean}\r
139 */\r
140jasmine.isArray_ = function(value) {\r
141 return jasmine.isA_("Array", value);\r
142};\r
143\r
144/**\r
145 * @ignore\r
146 * @private\r
147 * @param value\r
148 * @returns {Boolean}\r
149 */\r
150jasmine.isString_ = function(value) {\r
151 return jasmine.isA_("String", value);\r
152};\r
153\r
154/**\r
155 * @ignore\r
156 * @private\r
157 * @param value\r
158 * @returns {Boolean}\r
159 */\r
160jasmine.isNumber_ = function(value) {\r
161 return jasmine.isA_("Number", value);\r
162};\r
163\r
164/**\r
165 * @ignore\r
166 * @private\r
167 * @param {String} typeName\r
168 * @param value\r
169 * @returns {Boolean}\r
170 */\r
171jasmine.isA_ = function(typeName, value) {\r
172 return Object.prototype.toString.apply(value) === '[object ' + typeName + ']';\r
173};\r
174\r
175/**\r
176 * Pretty printer for expecations. Takes any object and turns it into a human-readable string.\r
177 *\r
178 * @param value {Object} an object to be outputted\r
179 * @returns {String}\r
180 */\r
181jasmine.pp = function(value) {\r
182 var stringPrettyPrinter = new jasmine.StringPrettyPrinter();\r
183 stringPrettyPrinter.format(value);\r
184 return stringPrettyPrinter.string;\r
185};\r
186\r
187/**\r
188 * Returns true if the object is a DOM Node.\r
189 *\r
190 * @param {Object} obj object to check\r
191 * @returns {Boolean}\r
192 */\r
193jasmine.isDomNode = function(obj) {\r
194 return obj.nodeType > 0;\r
195};\r
196\r
197/**\r
198 * Returns a matchable 'generic' object of the class type. For use in expecations of type when values don't matter.\r
199 *\r
200 * @example\r
201 * // don't care about which function is passed in, as long as it's a function\r
202 * expect(mySpy).toHaveBeenCalledWith(jasmine.any(Function));\r
203 *\r
204 * @param {Class} clazz\r
205 * @returns matchable object of the type clazz\r
206 */\r
207jasmine.any = function(clazz) {\r
208 return new jasmine.Matchers.Any(clazz);\r
209};\r
210\r
211/**\r
212 * Returns a matchable subset of a JSON object. For use in expectations when you don't care about all of the\r
213 * attributes on the object.\r
214 *\r
215 * @example\r
216 * // don't care about any other attributes than foo.\r
217 * expect(mySpy).toHaveBeenCalledWith(jasmine.objectContaining({foo: "bar"});\r
218 *\r
219 * @param sample {Object} sample\r
220 * @returns matchable object for the sample\r
221 */\r
222jasmine.objectContaining = function (sample) {\r
223 return new jasmine.Matchers.ObjectContaining(sample);\r
224};\r
225\r
226/**\r
227 * Jasmine Spies are test doubles that can act as stubs, spies, fakes or when used in an expecation, mocks.\r
228 *\r
229 * Spies should be created in test setup, before expectations. They can then be checked, using the standard Jasmine\r
230 * expectation syntax. Spies can be checked if they were called or not and what the calling params were.\r
231 *\r
232 * A Spy has the following fields: wasCalled, callCount, mostRecentCall, and argsForCall (see docs).\r
233 *\r
234 * Spies are torn down at the end of every spec.\r
235 *\r
236 * Note: Do <b>not</b> call new jasmine.Spy() directly - a spy must be created using spyOn, jasmine.createSpy or jasmine.createSpyObj.\r
237 *\r
238 * @example\r
239 * // a stub\r
240 * var myStub = jasmine.createSpy('myStub'); // can be used anywhere\r
241 *\r
242 * // spy example\r
243 * var foo = {\r
244 * not: function(bool) { return !bool; }\r
245 * }\r
246 *\r
247 * // actual foo.not will not be called, execution stops\r
248 * spyOn(foo, 'not');\r
249\r
250 // foo.not spied upon, execution will continue to implementation\r
251 * spyOn(foo, 'not').andCallThrough();\r
252 *\r
253 * // fake example\r
254 * var foo = {\r
255 * not: function(bool) { return !bool; }\r
256 * }\r
257 *\r
258 * // foo.not(val) will return val\r
259 * spyOn(foo, 'not').andCallFake(function(value) {return value;});\r
260 *\r
261 * // mock example\r
262 * foo.not(7 == 7);\r
263 * expect(foo.not).toHaveBeenCalled();\r
264 * expect(foo.not).toHaveBeenCalledWith(true);\r
265 *\r
266 * @constructor\r
267 * @see spyOn, jasmine.createSpy, jasmine.createSpyObj\r
268 * @param {String} name\r
269 */\r
270jasmine.Spy = function(name) {\r
271 /**\r
272 * The name of the spy, if provided.\r
273 */\r
274 this.identity = name || 'unknown';\r
275 /**\r
276 * Is this Object a spy?\r
277 */\r
278 this.isSpy = true;\r
279 /**\r
280 * The actual function this spy stubs.\r
281 */\r
282 this.plan = function() {\r
283 };\r
284 /**\r
285 * Tracking of the most recent call to the spy.\r
286 * @example\r
287 * var mySpy = jasmine.createSpy('foo');\r
288 * mySpy(1, 2);\r
289 * mySpy.mostRecentCall.args = [1, 2];\r
290 */\r
291 this.mostRecentCall = {};\r
292\r
293 /**\r
294 * Holds arguments for each call to the spy, indexed by call count\r
295 * @example\r
296 * var mySpy = jasmine.createSpy('foo');\r
297 * mySpy(1, 2);\r
298 * mySpy(7, 8);\r
299 * mySpy.mostRecentCall.args = [7, 8];\r
300 * mySpy.argsForCall[0] = [1, 2];\r
301 * mySpy.argsForCall[1] = [7, 8];\r
302 */\r
303 this.argsForCall = [];\r
304 this.calls = [];\r
305};\r
306\r
307/**\r
308 * Tells a spy to call through to the actual implemenatation.\r
309 *\r
310 * @example\r
311 * var foo = {\r
312 * bar: function() { // do some stuff }\r
313 * }\r
314 *\r
315 * // defining a spy on an existing property: foo.bar\r
316 * spyOn(foo, 'bar').andCallThrough();\r
317 */\r
318jasmine.Spy.prototype.andCallThrough = function() {\r
319 this.plan = this.originalValue;\r
320 return this;\r
321};\r
322\r
323/**\r
324 * For setting the return value of a spy.\r
325 *\r
326 * @example\r
327 * // defining a spy from scratch: foo() returns 'baz'\r
328 * var foo = jasmine.createSpy('spy on foo').andReturn('baz');\r
329 *\r
330 * // defining a spy on an existing property: foo.bar() returns 'baz'\r
331 * spyOn(foo, 'bar').andReturn('baz');\r
332 *\r
333 * @param {Object} value\r
334 */\r
335jasmine.Spy.prototype.andReturn = function(value) {\r
336 this.plan = function() {\r
337 return value;\r
338 };\r
339 return this;\r
340};\r
341\r
342/**\r
343 * For throwing an exception when a spy is called.\r
344 *\r
345 * @example\r
346 * // defining a spy from scratch: foo() throws an exception w/ message 'ouch'\r
347 * var foo = jasmine.createSpy('spy on foo').andThrow('baz');\r
348 *\r
349 * // defining a spy on an existing property: foo.bar() throws an exception w/ message 'ouch'\r
350 * spyOn(foo, 'bar').andThrow('baz');\r
351 *\r
352 * @param {String} exceptionMsg\r
353 */\r
354jasmine.Spy.prototype.andThrow = function(exceptionMsg) {\r
355 this.plan = function() {\r
356 throw exceptionMsg;\r
357 };\r
358 return this;\r
359};\r
360\r
361/**\r
362 * Calls an alternate implementation when a spy is called.\r
363 *\r
364 * @example\r
365 * var baz = function() {\r
366 * // do some stuff, return something\r
367 * }\r
368 * // defining a spy from scratch: foo() calls the function baz\r
369 * var foo = jasmine.createSpy('spy on foo').andCall(baz);\r
370 *\r
371 * // defining a spy on an existing property: foo.bar() calls an anonymnous function\r
372 * spyOn(foo, 'bar').andCall(function() { return 'baz';} );\r
373 *\r
374 * @param {Function} fakeFunc\r
375 */\r
376jasmine.Spy.prototype.andCallFake = function(fakeFunc) {\r
377 this.plan = fakeFunc;\r
378 return this;\r
379};\r
380\r
381/**\r
382 * Resets all of a spy's the tracking variables so that it can be used again.\r
383 *\r
384 * @example\r
385 * spyOn(foo, 'bar');\r
386 *\r
387 * foo.bar();\r
388 *\r
389 * expect(foo.bar.callCount).toEqual(1);\r
390 *\r
391 * foo.bar.reset();\r
392 *\r
393 * expect(foo.bar.callCount).toEqual(0);\r
394 */\r
395jasmine.Spy.prototype.reset = function() {\r
396 this.wasCalled = false;\r
397 this.callCount = 0;\r
398 this.argsForCall = [];\r
399 this.calls = [];\r
400 this.mostRecentCall = {};\r
401};\r
402\r
403jasmine.createSpy = function(name) {\r
404\r
405 var spyObj = function() {\r
406 spyObj.wasCalled = true;\r
407 spyObj.callCount++;\r
408 var args = jasmine.util.argsToArray(arguments);\r
409 spyObj.mostRecentCall.object = this;\r
410 spyObj.mostRecentCall.args = args;\r
411 spyObj.argsForCall.push(args);\r
412 spyObj.calls.push({object: this, args: args});\r
413 return spyObj.plan.apply(this, arguments);\r
414 };\r
415\r
416 var spy = new jasmine.Spy(name);\r
417\r
418 for (var prop in spy) {\r
419 spyObj[prop] = spy[prop];\r
420 }\r
421\r
422 spyObj.reset();\r
423\r
424 return spyObj;\r
425};\r
426\r
427/**\r
428 * Determines whether an object is a spy.\r
429 *\r
430 * @param {jasmine.Spy|Object} putativeSpy\r
431 * @returns {Boolean}\r
432 */\r
433jasmine.isSpy = function(putativeSpy) {\r
434 return putativeSpy && putativeSpy.isSpy;\r
435};\r
436\r
437/**\r
438 * Creates a more complicated spy: an Object that has every property a function that is a spy. Used for stubbing something\r
439 * large in one call.\r
440 *\r
441 * @param {String} baseName name of spy class\r
442 * @param {Array} methodNames array of names of methods to make spies\r
443 */\r
444jasmine.createSpyObj = function(baseName, methodNames) {\r
445 if (!jasmine.isArray_(methodNames) || methodNames.length === 0) {\r
446 throw new Error('createSpyObj requires a non-empty array of method names to create spies for');\r
447 }\r
448 var obj = {};\r
449 for (var i = 0; i < methodNames.length; i++) {\r
450 obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]);\r
451 }\r
452 return obj;\r
453};\r
454\r
455/**\r
456 * All parameters are pretty-printed and concatenated together, then written to the current spec's output.\r
457 *\r
458 * Be careful not to leave calls to <code>jasmine.log</code> in production code.\r
459 */\r
460jasmine.log = function() {\r
461 var spec = jasmine.getEnv().currentSpec;\r
462 spec.log.apply(spec, arguments);\r
463};\r
464\r
465/**\r
466 * Function that installs a spy on an existing object's method name. Used within a Spec to create a spy.\r
467 *\r
468 * @example\r
469 * // spy example\r
470 * var foo = {\r
471 * not: function(bool) { return !bool; }\r
472 * }\r
473 * spyOn(foo, 'not'); // actual foo.not will not be called, execution stops\r
474 *\r
475 * @see jasmine.createSpy\r
476 * @param obj\r
477 * @param methodName\r
478 * @return {jasmine.Spy} a Jasmine spy that can be chained with all spy methods\r
479 */\r
480var spyOn = function(obj, methodName) {\r
481 return jasmine.getEnv().currentSpec.spyOn(obj, methodName);\r
482};\r
483if (isCommonJS) exports.spyOn = spyOn;\r
484\r
485/**\r
486 * Creates a Jasmine spec that will be added to the current suite.\r
487 *\r
488 * // TODO: pending tests\r
489 *\r
490 * @example\r
491 * it('should be true', function() {\r
492 * expect(true).toEqual(true);\r
493 * });\r
494 *\r
495 * @param {String} desc description of this specification\r
496 * @param {Function} func defines the preconditions and expectations of the spec\r
497 */\r
498var it = function(desc, func) {\r
499 return jasmine.getEnv().it(desc, func);\r
500};\r
501if (isCommonJS) exports.it = it;\r
502\r
503/**\r
504 * Creates a <em>disabled</em> Jasmine spec.\r
505 *\r
506 * A convenience method that allows existing specs to be disabled temporarily during development.\r
507 *\r
508 * @param {String} desc description of this specification\r
509 * @param {Function} func defines the preconditions and expectations of the spec\r
510 */\r
511var xit = function(desc, func) {\r
512 return jasmine.getEnv().xit(desc, func);\r
513};\r
514if (isCommonJS) exports.xit = xit;\r
515\r
516/**\r
517 * Starts a chain for a Jasmine expectation.\r
518 *\r
519 * It is passed an Object that is the actual value and should chain to one of the many\r
520 * jasmine.Matchers functions.\r
521 *\r
522 * @param {Object} actual Actual value to test against and expected value\r
523 * @return {jasmine.Matchers}\r
524 */\r
525var expect = function(actual) {\r
526 return jasmine.getEnv().currentSpec.expect(actual);\r
527};\r
528if (isCommonJS) exports.expect = expect;\r
529\r
530/**\r
531 * Defines part of a jasmine spec. Used in cominbination with waits or waitsFor in asynchrnous specs.\r
532 *\r
533 * @param {Function} func Function that defines part of a jasmine spec.\r
534 */\r
535var runs = function(func) {\r
536 jasmine.getEnv().currentSpec.runs(func);\r
537};\r
538if (isCommonJS) exports.runs = runs;\r
539\r
540/**\r
541 * Waits a fixed time period before moving to the next block.\r
542 *\r
543 * @deprecated Use waitsFor() instead\r
544 * @param {Number} timeout milliseconds to wait\r
545 */\r
546var waits = function(timeout) {\r
547 jasmine.getEnv().currentSpec.waits(timeout);\r
548};\r
549if (isCommonJS) exports.waits = waits;\r
550\r
551/**\r
552 * Waits for the latchFunction to return true before proceeding to the next block.\r
553 *\r
554 * @param {Function} latchFunction\r
555 * @param {String} optional_timeoutMessage\r
556 * @param {Number} optional_timeout\r
557 * @param {Number} optional_timeout_increment\r
558 */\r
559var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout, optional_timeout_increment) {\r
560 jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);\r
561};\r
562if (isCommonJS) exports.waitsFor = waitsFor;\r
563\r
564/**\r
565 * A function that is called before each spec in a suite.\r
566 *\r
567 * Used for spec setup, including validating assumptions.\r
568 *\r
569 * @param {Function} beforeEachFunction\r
570 */\r
571var beforeEach = function(beforeEachFunction) {\r
572 jasmine.getEnv().beforeEach(beforeEachFunction);\r
573};\r
574if (isCommonJS) exports.beforeEach = beforeEach;\r
575\r
576/**\r
577 * A function that is called after each spec in a suite.\r
578 *\r
579 * Used for restoring any state that is hijacked during spec execution.\r
580 *\r
581 * @param {Function} afterEachFunction\r
582 */\r
583var afterEach = function(afterEachFunction) {\r
584 jasmine.getEnv().afterEach(afterEachFunction);\r
585};\r
586if (isCommonJS) exports.afterEach = afterEach;\r
587\r
588/**\r
589 * Defines a suite of specifications.\r
590 *\r
591 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared\r
592 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization\r
593 * of setup in some tests.\r
594 *\r
595 * @example\r
596 * // TODO: a simple suite\r
597 *\r
598 * // TODO: a simple suite with a nested describe block\r
599 *\r
600 * @param {String} description A string, usually the class under test.\r
601 * @param {Function} specDefinitions function that defines several specs.\r
602 */\r
603var describe = function(description, specDefinitions) {\r
604 return jasmine.getEnv().describe(description, specDefinitions);\r
605};\r
606if (isCommonJS) exports.describe = describe;\r
607\r
608/**\r
609 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.\r
610 *\r
611 * @param {String} description A string, usually the class under test.\r
612 * @param {Function} specDefinitions function that defines several specs.\r
613 */\r
614var xdescribe = function(description, specDefinitions) {\r
615 return jasmine.getEnv().xdescribe(description, specDefinitions);\r
616};\r
617if (isCommonJS) exports.xdescribe = xdescribe;\r
618\r
619\r
620// Provide the XMLHttpRequest class for IE 5.x-6.x:\r
621jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {\r
622 function tryIt(f) {\r
623 try {\r
624 return f();\r
625 } catch(e) {\r
626 }\r
627 return null;\r
628 }\r
629\r
630 var xhr = tryIt(function() {\r
631 return new ActiveXObject("Msxml2.XMLHTTP.6.0");\r
632 }) ||\r
633 tryIt(function() {\r
634 return new ActiveXObject("Msxml2.XMLHTTP.3.0");\r
635 }) ||\r
636 tryIt(function() {\r
637 return new ActiveXObject("Msxml2.XMLHTTP");\r
638 }) ||\r
639 tryIt(function() {\r
640 return new ActiveXObject("Microsoft.XMLHTTP");\r
641 });\r
642\r
643 if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");\r
644\r
645 return xhr;\r
646} : XMLHttpRequest;\r
647jasmine.MAX_PRETTY_PRINT_DEPTH = 1;\r
648\r
649jasmine.hashes = {};\r
650\r
651jasmine.hashString = function (s, hash) {\r
652 hash = hash || 0;\r
653 \r
654 // see http://www.cse.yorku.ca/~oz/hash.html\r
655 for (var c, i = 0, n = s.length; i < n; ++i) {\r
656 c = s.charCodeAt(i);\r
657 hash = c + (hash << 6) + (hash << 16) - hash;\r
658 }\r
659 \r
660 if (jasmine.hashes[hash]) {\r
661 jasmine.getEnv().reporter.log("Identical hash detected: " + s);\r
662 }\r
663 \r
664 jasmine.hashes[hash] = true;\r
665 return hash;\r
666};\r
667\r
668jasmine.toMap = function (items) {\r
669 var map = {},\r
670 k;\r
671\r
672 for (k = items.length; k--; ) {\r
673 map[items[k]] = true;\r
674 }\r
675\r
676 return map;\r
677};\r
678\r
679// Override jasmine to track the spy call results as well\r
680// as their invocation arguments\r
681jasmine.createSpy = function(name) {\r
682\r
683 var spyObj = function() {\r
684 spyObj.wasCalled = true;\r
685 spyObj.callCount++;\r
686 \r
687 var args = jasmine.util.argsToArray(arguments);\r
688 var result = spyObj.plan.apply(this, arguments);\r
689 \r
690 spyObj.mostRecentCall.object = spyObj.mostRecentCall.scope = this;\r
691 spyObj.mostRecentCall.args = args;\r
692 spyObj.mostRecentCall.result = result;\r
693 \r
694 spyObj.argsForCall.push(args);\r
695 \r
696 spyObj.calls.push({object: this, scope: this, args: args, result: result});\r
697 \r
698 return result;\r
699 };\r
700\r
701 var spy = new jasmine.Spy(name);\r
702\r
703 for (var prop in spy) {\r
704 spyObj[prop] = spy[prop];\r
705 }\r
706\r
707 spyObj.reset();\r
708\r
709 return spyObj;\r
710};\r
711\r
712jasmine.setCurrentScript = function(file){\r
713 if(typeof Ext !== "undefined" && Ext.cmd && Ext.cmd.api && Ext.cmd.api.adapter) {\r
714 Ext.cmd.api.adapter.setCurrentScript(file);\r
715 }\r
716};\r
717\r
718jasmine.getCurrentScript = function() {\r
719 if(typeof Ext !== "undefined" && Ext.cmd && Ext.cmd.api && Ext.cmd.api.adapter) {\r
720 return Ext.cmd.api.adapter.getCurrentScript();\r
721 }\r
722 return null;\r
723};\r
724\r
725jasmine.setOptions = function(jsonString) {\r
726 jasmine._options = JSON.parse(jsonString);\r
727};\r
728\r
729jasmine.getOptions = function() {\r
730 return jasmine._options || {};\r
731};\r
732\r
733jasmine.initDebug = function() {\r
734 var spec = jasmine.getOptions().spec;\r
735 if (spec) {\r
736 var specId = parseInt(spec);\r
737 this.getEnv().specFilter = function(spec) {\r
738 if (spec.id === specId) {\r
739 spec.debugBlocks = true;\r
740 return true;\r
741 } else {\r
742 return false;\r
743 }\r
744 }\r
745 }\r
746};\r
747\r
748jasmine.generateDebuggableBlock = function(fn) {\r
749 return function() {\r
750 debugger;\r
751 /* Step into the function below */\r
752 fn.apply(this, arguments);\r
753 };\r
754};\r
755\r
756jasmine.showDebugPrompt = function(callback) {\r
757 if (navigator.userAgent.toLowerCase().match(/chrome|safari|msie/)) {\r
758 var div = document.createElement("div");\r
759 \r
760 div.setAttribute("style",[ \r
761 "background:#E5E4E2;",\r
762 "z-index:99999;",\r
763 "position:absolute;",\r
764 "left:25%;",\r
765 "right:25%;",\r
766 "top:100px;",\r
767 "border-radius: 5px;",\r
768 "border:1px solid #777;",\r
769 "text-align:center;",\r
770 "box-shadow: 5px 5px 5px #888;"\r
771 ].join(""));\r
772 \r
773 div.innerHTML = [\r
774 '<p>Open the developer tools to debug and press ok.</p>',\r
775 '<button id="sencha-debug-button">OK</button>',\r
776 '<p></p>'\r
777 ].join("");\r
778 \r
779 document.body.appendChild(div);\r
780 \r
781 var button = document.getElementById("sencha-debug-button");\r
782 \r
783 var onClick = function() {\r
784 if (button.removeEventListener) {\r
785 button.removeEventListener("click", onClick, false);\r
786 } else {\r
787 button.detachEvent("onmousedown", onClick);\r
788 }\r
789 document.body.removeChild(div);\r
790 div = null;\r
791 callback();\r
792 };\r
793 \r
794 if (button.addEventListener) {\r
795 button.addEventListener("click", onClick, false);\r
796 } else {\r
797 button.attachEvent("onmousedown", onClick);\r
798 }\r
799 } else {\r
800 callback();\r
801 }\r
802};\r
803\r
804jasmine.getByIds = function (items, ids) {\r
805 var result = [],\r
806 length = items.length,\r
807 i, id, item;\r
808\r
809 for (i = 0; i < length; ++i) {\r
810 item = items[i];\r
811 id = item.id;\r
812 if (ids[id]) {\r
813 result.push(item);\r
814 }\r
815 }\r
816\r
817 return result;\r
818};\r
819\r
820var specFor = function(object, specForFn) {\r
821 jasmine.getEnv().specFor(object, specForFn);\r
822};\r
823\r
824var xspecFor = function(object, specForFn) {\r
825 jasmine.getEnv().xspecFor(object, specForFn);\r
826};\r
827\r
828var xdescribe = function(description, specDefinitions, coverageFile) {\r
829 return jasmine.getEnv().describe(description, specDefinitions, coverageFile).disable();\r
830};\r
831\r
832var xit = function(desc, func) {\r
833 return jasmine.getEnv().it(desc, func).disable();\r
834};\r
835/**\r
836 * @namespace\r
837 */\r
838jasmine.util = {};\r
839\r
840/**\r
841 * Declare that a child class inherit it's prototype from the parent class.\r
842 *\r
843 * @private\r
844 * @param {Function} childClass\r
845 * @param {Function} parentClass\r
846 */\r
847jasmine.util.inherit = function(childClass, parentClass) {\r
848 /**\r
849 * @private\r
850 */\r
851 var subclass = function() {\r
852 };\r
853 subclass.prototype = parentClass.prototype;\r
854 childClass.prototype = new subclass();\r
855};\r
856\r
857jasmine.util.formatException = function(e) {\r
858 var lineNumber;\r
859 if (e.line) {\r
860 lineNumber = e.line;\r
861 }\r
862 else if (e.lineNumber) {\r
863 lineNumber = e.lineNumber;\r
864 }\r
865\r
866 var file;\r
867\r
868 if (e.sourceURL) {\r
869 file = e.sourceURL;\r
870 }\r
871 else if (e.fileName) {\r
872 file = e.fileName;\r
873 }\r
874\r
875 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\r
876\r
877 if (file && lineNumber) {\r
878 message += ' in ' + file + ' (line ' + lineNumber + ')';\r
879 }\r
880\r
881 return message;\r
882};\r
883\r
884jasmine.util.htmlEscape = function(str) {\r
885 if (!str) return str;\r
886 return str.replace(/&/g, '&amp;')\r
887 .replace(/</g, '&lt;')\r
888 .replace(/>/g, '&gt;');\r
889};\r
890\r
891jasmine.util.argsToArray = function(args) {\r
892 var arrayOfArgs = [];\r
893 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);\r
894 return arrayOfArgs;\r
895};\r
896\r
897jasmine.util.extend = function(destination, source) {\r
898 for (var property in source) destination[property] = source[property];\r
899 return destination;\r
900};\r
901jasmine.util.getOrigin = function() {\r
902 var port = window.location.port;\r
903 var origin;\r
904\r
905 origin = window.location.protocol + "//" + window.location.hostname;\r
906\r
907 if (port) {\r
908 origin += ":" + port;\r
909 }\r
910\r
911 return origin;\r
912};\r
913\r
914jasmine.util.getFileFromContextMapping = function(file) {\r
915 var contextMapping = jasmine.contextMapping;\r
916 if (file && contextMapping) {\r
917 var origin = jasmine.util.getOrigin();\r
918 for (var context in contextMapping) {\r
919 file = file.replace(origin + context, contextMapping[context]);\r
920 }\r
921 }\r
922 return file;\r
923};\r
924\r
925jasmine.util.formatException = function(e) {\r
926 var lineNumber;\r
927 if (e.line) {\r
928 lineNumber = e.line;\r
929 }\r
930 else if (e.lineNumber) {\r
931 lineNumber = e.lineNumber;\r
932 }\r
933\r
934 var file;\r
935\r
936 if (e.sourceURL) {\r
937 file = e.sourceURL;\r
938 }\r
939 else if (e.fileName) {\r
940 file = e.fileName;\r
941 }\r
942\r
943 file = jasmine.util.getFileFromContextMapping(file);\r
944 \r
945 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\r
946\r
947 if (file && lineNumber) {\r
948 message += ' in ' + file + ' (line ' + lineNumber + ')';\r
949 }\r
950\r
951 return message;\r
952};/**\r
953 * Environment for Jasmine\r
954 *\r
955 * @constructor\r
956 */\r
957jasmine.Env = function() {\r
958 this.currentSpec = null;\r
959 this.currentSuite = null;\r
960 this.currentRunner_ = new jasmine.Runner(this);\r
961\r
962 this.reporter = new jasmine.MultiReporter();\r
963\r
964 this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;\r
965 this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;\r
966 this.lastUpdate = 0;\r
967 this.specFilter = function() {\r
968 return true;\r
969 };\r
970\r
971 this.nextSpecId_ = 0;\r
972 this.nextSuiteId_ = 0;\r
973 this.equalityTesters_ = [];\r
974\r
975 // wrap matchers\r
976 this.matchersClass = function() {\r
977 jasmine.Matchers.apply(this, arguments);\r
978 };\r
979 jasmine.util.inherit(this.matchersClass, jasmine.Matchers);\r
980\r
981 jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);\r
982};\r
983\r
984\r
985jasmine.Env.prototype.setTimeout = jasmine.setTimeout;\r
986jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;\r
987jasmine.Env.prototype.setInterval = jasmine.setInterval;\r
988jasmine.Env.prototype.clearInterval = jasmine.clearInterval;\r
989\r
990/**\r
991 * @returns an object containing jasmine version build info, if set.\r
992 */\r
993jasmine.Env.prototype.version = function () {\r
994 if (jasmine.version_) {\r
995 return jasmine.version_;\r
996 } else {\r
997 throw new Error('Version not set');\r
998 }\r
999};\r
1000\r
1001/**\r
1002 * @returns string containing jasmine version build info, if set.\r
1003 */\r
1004jasmine.Env.prototype.versionString = function() {\r
1005 if (!jasmine.version_) {\r
1006 return "version unknown";\r
1007 }\r
1008\r
1009 var version = this.version();\r
1010 var versionString = version.major + "." + version.minor + "." + version.build;\r
1011 if (version.release_candidate) {\r
1012 versionString += ".rc" + version.release_candidate;\r
1013 }\r
1014 versionString += " revision " + version.revision;\r
1015 return versionString;\r
1016};\r
1017\r
1018/**\r
1019 * @returns a sequential integer starting at 0\r
1020 */\r
1021jasmine.Env.prototype.nextSpecId = function () {\r
1022 return this.nextSpecId_++;\r
1023};\r
1024\r
1025/**\r
1026 * @returns a sequential integer starting at 0\r
1027 */\r
1028jasmine.Env.prototype.nextSuiteId = function () {\r
1029 return this.nextSuiteId_++;\r
1030};\r
1031\r
1032/**\r
1033 * Register a reporter to receive status updates from Jasmine.\r
1034 * @param {jasmine.Reporter} reporter An object which will receive status updates.\r
1035 */\r
1036jasmine.Env.prototype.addReporter = function(reporter) {\r
1037 this.reporter.addReporter(reporter);\r
1038};\r
1039\r
1040jasmine.Env.prototype.execute = function() {\r
1041 this.currentRunner_.execute();\r
1042};\r
1043\r
1044jasmine.Env.prototype.describe = function(description, specDefinitions) {\r
1045 var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);\r
1046\r
1047 var parentSuite = this.currentSuite;\r
1048 if (parentSuite) {\r
1049 parentSuite.add(suite);\r
1050 } else {\r
1051 this.currentRunner_.add(suite);\r
1052 }\r
1053\r
1054 this.currentSuite = suite;\r
1055\r
1056 var declarationError = null;\r
1057 try {\r
1058 specDefinitions.call(suite);\r
1059 } catch(e) {\r
1060 declarationError = e;\r
1061 }\r
1062\r
1063 if (declarationError) {\r
1064 this.it("encountered a declaration exception", function() {\r
1065 throw declarationError;\r
1066 });\r
1067 }\r
1068\r
1069 this.currentSuite = parentSuite;\r
1070\r
1071 return suite;\r
1072};\r
1073\r
1074jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {\r
1075 if (this.currentSuite) {\r
1076 this.currentSuite.beforeEach(beforeEachFunction);\r
1077 } else {\r
1078 this.currentRunner_.beforeEach(beforeEachFunction);\r
1079 }\r
1080};\r
1081\r
1082jasmine.Env.prototype.currentRunner = function () {\r
1083 return this.currentRunner_;\r
1084};\r
1085\r
1086jasmine.Env.prototype.afterEach = function(afterEachFunction) {\r
1087 if (this.currentSuite) {\r
1088 this.currentSuite.afterEach(afterEachFunction);\r
1089 } else {\r
1090 this.currentRunner_.afterEach(afterEachFunction);\r
1091 }\r
1092\r
1093};\r
1094\r
1095jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {\r
1096 return {\r
1097 execute: function() {\r
1098 }\r
1099 };\r
1100};\r
1101\r
1102jasmine.Env.prototype.it = function(description, func) {\r
1103 var spec = new jasmine.Spec(this, this.currentSuite, description);\r
1104 this.currentSuite.add(spec);\r
1105 this.currentSpec = spec;\r
1106\r
1107 if (func) {\r
1108 spec.runs(func);\r
1109 }\r
1110\r
1111 return spec;\r
1112};\r
1113\r
1114jasmine.Env.prototype.xit = function(desc, func) {\r
1115 return {\r
1116 id: this.nextSpecId(),\r
1117 runs: function() {\r
1118 }\r
1119 };\r
1120};\r
1121\r
1122jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {\r
1123 if (a.source != b.source)\r
1124 mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");\r
1125\r
1126 if (a.ignoreCase != b.ignoreCase)\r
1127 mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");\r
1128\r
1129 if (a.global != b.global)\r
1130 mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");\r
1131\r
1132 if (a.multiline != b.multiline)\r
1133 mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");\r
1134\r
1135 if (a.sticky != b.sticky)\r
1136 mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");\r
1137\r
1138 return (mismatchValues.length === 0);\r
1139};\r
1140\r
1141jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {\r
1142 if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {\r
1143 return true;\r
1144 }\r
1145\r
1146 a.__Jasmine_been_here_before__ = b;\r
1147 b.__Jasmine_been_here_before__ = a;\r
1148\r
1149 var hasKey = function(obj, keyName) {\r
1150 return obj !== null && obj[keyName] !== jasmine.undefined;\r
1151 };\r
1152\r
1153 for (var property in b) {\r
1154 if (!hasKey(a, property) && hasKey(b, property)) {\r
1155 mismatchKeys.push("expected has key '" + property + "', but missing from actual.");\r
1156 }\r
1157 }\r
1158 for (property in a) {\r
1159 if (!hasKey(b, property) && hasKey(a, property)) {\r
1160 mismatchKeys.push("expected missing key '" + property + "', but present in actual.");\r
1161 }\r
1162 }\r
1163 for (property in b) {\r
1164 if (property == '__Jasmine_been_here_before__') continue;\r
1165 if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {\r
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.");\r
1167 }\r
1168 }\r
1169\r
1170 if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {\r
1171 mismatchValues.push("arrays were not the same length");\r
1172 }\r
1173\r
1174 delete a.__Jasmine_been_here_before__;\r
1175 delete b.__Jasmine_been_here_before__;\r
1176 return (mismatchKeys.length === 0 && mismatchValues.length === 0);\r
1177};\r
1178\r
1179jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {\r
1180 mismatchKeys = mismatchKeys || [];\r
1181 mismatchValues = mismatchValues || [];\r
1182\r
1183 for (var i = 0; i < this.equalityTesters_.length; i++) {\r
1184 var equalityTester = this.equalityTesters_[i];\r
1185 var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);\r
1186 if (result !== jasmine.undefined) return result;\r
1187 }\r
1188\r
1189 if (a === b) return true;\r
1190\r
1191 if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {\r
1192 return (a == jasmine.undefined && b == jasmine.undefined);\r
1193 }\r
1194\r
1195 if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {\r
1196 return a === b;\r
1197 }\r
1198\r
1199 if (a instanceof Date && b instanceof Date) {\r
1200 return a.getTime() == b.getTime();\r
1201 }\r
1202\r
1203 if (a.jasmineMatches) {\r
1204 return a.jasmineMatches(b);\r
1205 }\r
1206\r
1207 if (b.jasmineMatches) {\r
1208 return b.jasmineMatches(a);\r
1209 }\r
1210\r
1211 if (a instanceof jasmine.Matchers.ObjectContaining) {\r
1212 return a.matches(b);\r
1213 }\r
1214\r
1215 if (b instanceof jasmine.Matchers.ObjectContaining) {\r
1216 return b.matches(a);\r
1217 }\r
1218\r
1219 if (jasmine.isString_(a) && jasmine.isString_(b)) {\r
1220 return (a == b);\r
1221 }\r
1222\r
1223 if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {\r
1224 return (a == b);\r
1225 }\r
1226\r
1227 if (a instanceof RegExp && b instanceof RegExp) {\r
1228 return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);\r
1229 }\r
1230\r
1231 if (typeof a === "object" && typeof b === "object") {\r
1232 return this.compareObjects_(a, b, mismatchKeys, mismatchValues);\r
1233 }\r
1234\r
1235 //Straight check\r
1236 return (a === b);\r
1237};\r
1238\r
1239jasmine.Env.prototype.contains_ = function(haystack, needle) {\r
1240 if (jasmine.isArray_(haystack)) {\r
1241 for (var i = 0; i < haystack.length; i++) {\r
1242 if (this.equals_(haystack[i], needle)) return true;\r
1243 }\r
1244 return false;\r
1245 }\r
1246 return haystack.indexOf(needle) >= 0;\r
1247};\r
1248\r
1249jasmine.Env.prototype.addEqualityTester = function(equalityTester) {\r
1250 this.equalityTesters_.push(equalityTester);\r
1251};\r
1252/**\r
1253 * Basic browsers detection.\r
1254 */\r
1255jasmine.browser = {};\r
1256jasmine.browser.isIE = !!window.ActiveXObject;\r
1257jasmine.browser.isIE6 = jasmine.browser.isIE && !window.XMLHttpRequest;\r
1258jasmine.browser.isIE7 = jasmine.browser.isIE && !!window.XMLHttpRequest && !document.documentMode;\r
1259jasmine.browser.isIE8 = jasmine.browser.isIE && !!window.XMLHttpRequest && !!document.documentMode && !window.performance;\r
1260jasmine.browser.isIE9 = jasmine.browser.isIE && !!window.performance;\r
1261jasmine.browser.isSafari3 = /safari/.test(navigator.userAgent.toLowerCase()) && /version\/3/.test(navigator.userAgent.toLowerCase());\r
1262jasmine.browser.isOpera = !!window.opera;\r
1263jasmine.browser.isOpera11 = jasmine.browser.isOpera && parseInt(window.opera.version(), 10) > 10;\r
1264\r
1265jasmine.array = {};\r
1266\r
1267 /**\r
1268 * Checks whether or not the specified item exists in the array.\r
1269 * Array.prototype.indexOf is missing in Internet Explorer, unfortunately.\r
1270 * We always have to use this static method instead for consistency\r
1271 * @param {Array} array The array to check\r
1272 * @param {Mixed} item The item to look for\r
1273 * @param {Number} from (Optional) The index at which to begin the search\r
1274 * @return {Number} The index of item in the array (or -1 if it is not found)\r
1275 */\r
1276jasmine.array.indexOf = function(array, item, from){\r
1277 if (array.indexOf) {\r
1278 return array.indexOf(item, from);\r
1279 }\r
1280 \r
1281 var i, length = array.length;\r
1282\r
1283 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){\r
1284 if (array[i] === item) {\r
1285 return i;\r
1286 }\r
1287 }\r
1288\r
1289 return -1;\r
1290};\r
1291\r
1292 /**\r
1293 * Removes the specified item from the array. If the item is not found nothing happens.\r
1294 * @param {Array} array The array\r
1295 * @param {Mixed} item The item to remove\r
1296 * @return {Array} The passed array itself\r
1297 */\r
1298jasmine.array.remove = function(array, item) {\r
1299 var index = this.indexOf(array, item);\r
1300\r
1301 if (index !== -1) {\r
1302 array.splice(index, 1);\r
1303 }\r
1304 \r
1305 return array;\r
1306};\r
1307\r
1308jasmine.Env.prototype.it = function(description, func, timeout) {\r
1309 var spec = new jasmine.Spec(this, this.currentSuite, description);\r
1310 this.currentSuite.add(spec);\r
1311 this.currentSpec = spec;\r
1312\r
1313 // override\r
1314 if (func) {\r
1315 func.typeName = 'it';\r
1316 var block = new jasmine.Block(spec.env, func, spec);\r
1317 block.timeout = parseInt(timeout);\r
1318 spec.addToQueue(block);\r
1319 }\r
1320 // end of override\r
1321 \r
1322 return spec;\r
1323};\r
1324\r
1325jasmine.Env.prototype.specFor = function(object, specForFn) {\r
1326 var index = 0,\r
1327 property;\r
1328\r
1329 for (property in object) {\r
1330 if (!object.hasOwnProperty(property)) {\r
1331 continue;\r
1332 }\r
1333 specForFn.call(this, property, object[property], index, object);\r
1334 index = index + 1;\r
1335 }\r
1336};\r
1337\r
1338jasmine.Env.prototype.xspecFor = function(object, specForFn) {};/** No-op base class for Jasmine reporters.\r
1339 *\r
1340 * @constructor\r
1341 */\r
1342jasmine.Reporter = function() {\r
1343};\r
1344\r
1345//noinspection JSUnusedLocalSymbols\r
1346jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {\r
1347};\r
1348\r
1349//noinspection JSUnusedLocalSymbols\r
1350jasmine.Reporter.prototype.reportRunnerResults = function(runner) {\r
1351};\r
1352\r
1353//noinspection JSUnusedLocalSymbols\r
1354jasmine.Reporter.prototype.reportSuiteResults = function(suite) {\r
1355};\r
1356\r
1357//noinspection JSUnusedLocalSymbols\r
1358jasmine.Reporter.prototype.reportSpecStarting = function(spec) {\r
1359};\r
1360\r
1361//noinspection JSUnusedLocalSymbols\r
1362jasmine.Reporter.prototype.reportSpecResults = function(spec) {\r
1363};\r
1364\r
1365//noinspection JSUnusedLocalSymbols\r
1366jasmine.Reporter.prototype.log = function(str) {\r
1367};\r
1368\r
1369/**\r
1370 * Blocks are functions with executable code that make up a spec.\r
1371 *\r
1372 * @constructor\r
1373 * @param {jasmine.Env} env\r
1374 * @param {Function} func\r
1375 * @param {jasmine.Spec} spec\r
1376 */\r
1377jasmine.Block = function(env, func, spec) {\r
1378 this.env = env;\r
1379 this.func = func;\r
1380 this.spec = spec;\r
1381};\r
1382\r
1383jasmine.Block.prototype.execute = function(onComplete) {\r
1384 if (!jasmine.CATCH_EXCEPTIONS) {\r
1385 this.func.apply(this.spec);\r
1386 }\r
1387 else {\r
1388 try {\r
1389 this.func.apply(this.spec);\r
1390 } catch (e) {\r
1391 this.spec.fail(e);\r
1392 }\r
1393 }\r
1394 onComplete();\r
1395};\r
1396jasmine.Block.prototype.execute = function(onComplete) {\r
1397 if (this.func.length === 1) {\r
1398 \r
1399 var timeOutId = setTimeout(function(){\r
1400 onComplete();\r
1401 }, this.timeout || jasmine.DEFAULT_TIMEOUT_INTERVAL);\r
1402 \r
1403 if (!jasmine.CATCH_EXCEPTIONS) {\r
1404 this.func.call(this.spec, function() {\r
1405 clearTimeout(timeOutId);\r
1406 onComplete();\r
1407 });\r
1408 } else {\r
1409 try {\r
1410 this.func.call(this.spec, function() {\r
1411 clearTimeout(timeOutId);\r
1412 onComplete();\r
1413 });\r
1414 } catch (e) {\r
1415 this.spec.fail(e);\r
1416 onComplete();\r
1417 }\r
1418 } \r
1419 } else {\r
1420 if (!jasmine.CATCH_EXCEPTIONS) {\r
1421 this.func.apply(this.spec);\r
1422 } else {\r
1423 try {\r
1424 this.func.apply(this.spec);\r
1425 } catch (e) {\r
1426 this.spec.fail(e);\r
1427 }\r
1428 }\r
1429 onComplete();\r
1430 }\r
1431};/** JavaScript API reporter.\r
1432 *\r
1433 * @constructor\r
1434 */\r
1435jasmine.JsApiReporter = function() {\r
1436 this.started = false;\r
1437 this.finished = false;\r
1438 this.suites_ = [];\r
1439 this.results_ = {};\r
1440};\r
1441\r
1442jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {\r
1443 this.started = true;\r
1444 var suites = runner.topLevelSuites();\r
1445 for (var i = 0; i < suites.length; i++) {\r
1446 var suite = suites[i];\r
1447 this.suites_.push(this.summarize_(suite));\r
1448 }\r
1449};\r
1450\r
1451jasmine.JsApiReporter.prototype.suites = function() {\r
1452 return this.suites_;\r
1453};\r
1454\r
1455jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {\r
1456 var isSuite = suiteOrSpec instanceof jasmine.Suite;\r
1457 var summary = {\r
1458 id: suiteOrSpec.id,\r
1459 name: suiteOrSpec.description,\r
1460 type: isSuite ? 'suite' : 'spec',\r
1461 children: []\r
1462 };\r
1463 \r
1464 if (isSuite) {\r
1465 var children = suiteOrSpec.children();\r
1466 for (var i = 0; i < children.length; i++) {\r
1467 summary.children.push(this.summarize_(children[i]));\r
1468 }\r
1469 }\r
1470 return summary;\r
1471};\r
1472\r
1473jasmine.JsApiReporter.prototype.results = function() {\r
1474 return this.results_;\r
1475};\r
1476\r
1477jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {\r
1478 return this.results_[specId];\r
1479};\r
1480\r
1481//noinspection JSUnusedLocalSymbols\r
1482jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {\r
1483 this.finished = true;\r
1484};\r
1485\r
1486//noinspection JSUnusedLocalSymbols\r
1487jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {\r
1488};\r
1489\r
1490//noinspection JSUnusedLocalSymbols\r
1491jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {\r
1492 this.results_[spec.id] = {\r
1493 messages: spec.results().getItems(),\r
1494 result: spec.results().failedCount > 0 ? "failed" : "passed"\r
1495 };\r
1496};\r
1497\r
1498//noinspection JSUnusedLocalSymbols\r
1499jasmine.JsApiReporter.prototype.log = function(str) {\r
1500};\r
1501\r
1502jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){\r
1503 var results = {};\r
1504 for (var i = 0; i < specIds.length; i++) {\r
1505 var specId = specIds[i];\r
1506 results[specId] = this.summarizeResult_(this.results_[specId]);\r
1507 }\r
1508 return results;\r
1509};\r
1510\r
1511jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){\r
1512 var summaryMessages = [];\r
1513 var messagesLength = result.messages.length;\r
1514 for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {\r
1515 var resultMessage = result.messages[messageIndex];\r
1516 summaryMessages.push({\r
1517 text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,\r
1518 passed: resultMessage.passed ? resultMessage.passed() : true,\r
1519 type: resultMessage.type,\r
1520 message: resultMessage.message,\r
1521 trace: {\r
1522 stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined\r
1523 }\r
1524 });\r
1525 }\r
1526\r
1527 return {\r
1528 result : result.result,\r
1529 messages : summaryMessages\r
1530 };\r
1531};\r
1532\r
1533/**\r
1534 * @constructor\r
1535 * @param {jasmine.Env} env\r
1536 * @param actual\r
1537 * @param {jasmine.Spec} spec\r
1538 */\r
1539jasmine.Matchers = function(env, actual, spec, opt_isNot) {\r
1540 this.env = env;\r
1541 this.actual = actual;\r
1542 this.spec = spec;\r
1543 this.isNot = opt_isNot || false;\r
1544 this.reportWasCalled_ = false;\r
1545};\r
1546\r
1547// todo: @deprecated as of Jasmine 0.11, remove soon [xw]\r
1548jasmine.Matchers.pp = function(str) {\r
1549 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");\r
1550};\r
1551\r
1552// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]\r
1553jasmine.Matchers.prototype.report = function(result, failing_message, details) {\r
1554 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");\r
1555};\r
1556\r
1557jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {\r
1558 for (var methodName in prototype) {\r
1559 if (methodName == 'report') continue;\r
1560 var orig = prototype[methodName];\r
1561 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);\r
1562 }\r
1563};\r
1564\r
1565jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {\r
1566 return function() {\r
1567 var matcherArgs = jasmine.util.argsToArray(arguments);\r
1568 var result = matcherFunction.apply(this, arguments);\r
1569\r
1570 if (this.isNot) {\r
1571 result = !result;\r
1572 }\r
1573\r
1574 if (this.reportWasCalled_) return result;\r
1575\r
1576 var message;\r
1577 if (!result) {\r
1578 if (this.message) {\r
1579 message = this.message.apply(this, arguments);\r
1580 if (jasmine.isArray_(message)) {\r
1581 message = message[this.isNot ? 1 : 0];\r
1582 }\r
1583 } else {\r
1584 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });\r
1585 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;\r
1586 if (matcherArgs.length > 0) {\r
1587 for (var i = 0; i < matcherArgs.length; i++) {\r
1588 if (i > 0) message += ",";\r
1589 message += " " + jasmine.pp(matcherArgs[i]);\r
1590 }\r
1591 }\r
1592 message += ".";\r
1593 }\r
1594 }\r
1595 var expectationResult = new jasmine.ExpectationResult({\r
1596 matcherName: matcherName,\r
1597 passed: result,\r
1598 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],\r
1599 actual: this.actual,\r
1600 message: message\r
1601 });\r
1602 this.spec.addMatcherResult(expectationResult);\r
1603 return jasmine.undefined;\r
1604 };\r
1605};\r
1606\r
1607\r
1608\r
1609\r
1610/**\r
1611 * toBe: compares the actual to the expected using ===\r
1612 * @param expected\r
1613 */\r
1614jasmine.Matchers.prototype.toBe = function(expected) {\r
1615 return this.actual === expected;\r
1616};\r
1617\r
1618/**\r
1619 * toNotBe: compares the actual to the expected using !==\r
1620 * @param expected\r
1621 * @deprecated as of 1.0. Use not.toBe() instead.\r
1622 */\r
1623jasmine.Matchers.prototype.toNotBe = function(expected) {\r
1624 return this.actual !== expected;\r
1625};\r
1626\r
1627/**\r
1628 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.\r
1629 *\r
1630 * @param expected\r
1631 */\r
1632jasmine.Matchers.prototype.toEqual = function(expected) {\r
1633 return this.env.equals_(this.actual, expected);\r
1634};\r
1635\r
1636/**\r
1637 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual\r
1638 * @param expected\r
1639 * @deprecated as of 1.0. Use not.toEqual() instead.\r
1640 */\r
1641jasmine.Matchers.prototype.toNotEqual = function(expected) {\r
1642 return !this.env.equals_(this.actual, expected);\r
1643};\r
1644\r
1645/**\r
1646 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes\r
1647 * a pattern or a String.\r
1648 *\r
1649 * @param expected\r
1650 */\r
1651jasmine.Matchers.prototype.toMatch = function(expected) {\r
1652 return new RegExp(expected).test(this.actual);\r
1653};\r
1654\r
1655/**\r
1656 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch\r
1657 * @param expected\r
1658 * @deprecated as of 1.0. Use not.toMatch() instead.\r
1659 */\r
1660jasmine.Matchers.prototype.toNotMatch = function(expected) {\r
1661 return !(new RegExp(expected).test(this.actual));\r
1662};\r
1663\r
1664/**\r
1665 * Matcher that compares the actual to jasmine.undefined.\r
1666 */\r
1667jasmine.Matchers.prototype.toBeDefined = function() {\r
1668 return (this.actual !== jasmine.undefined);\r
1669};\r
1670\r
1671/**\r
1672 * Matcher that compares the actual to jasmine.undefined.\r
1673 */\r
1674jasmine.Matchers.prototype.toBeUndefined = function() {\r
1675 return (this.actual === jasmine.undefined);\r
1676};\r
1677\r
1678/**\r
1679 * Matcher that compares the actual to null.\r
1680 */\r
1681jasmine.Matchers.prototype.toBeNull = function() {\r
1682 return (this.actual === null);\r
1683};\r
1684\r
1685/**\r
1686 * Matcher that compares the actual to NaN.\r
1687 */\r
1688jasmine.Matchers.prototype.toBeNaN = function() {\r
1689 this.message = function() {\r
1690 return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];\r
1691 };\r
1692\r
1693 return (this.actual !== this.actual);\r
1694};\r
1695\r
1696/**\r
1697 * Matcher that boolean not-nots the actual.\r
1698 */\r
1699jasmine.Matchers.prototype.toBeTruthy = function() {\r
1700 return !!this.actual;\r
1701};\r
1702\r
1703\r
1704/**\r
1705 * Matcher that boolean nots the actual.\r
1706 */\r
1707jasmine.Matchers.prototype.toBeFalsy = function() {\r
1708 return !this.actual;\r
1709};\r
1710\r
1711\r
1712/**\r
1713 * Matcher that checks to see if the actual, a Jasmine spy, was called.\r
1714 */\r
1715jasmine.Matchers.prototype.toHaveBeenCalled = function() {\r
1716 if (arguments.length > 0) {\r
1717 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');\r
1718 }\r
1719\r
1720 if (!jasmine.isSpy(this.actual)) {\r
1721 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1722 }\r
1723\r
1724 this.message = function() {\r
1725 return [\r
1726 "Expected spy " + this.actual.identity + " to have been called.",\r
1727 "Expected spy " + this.actual.identity + " not to have been called."\r
1728 ];\r
1729 };\r
1730\r
1731 return this.actual.wasCalled;\r
1732};\r
1733\r
1734/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */\r
1735jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;\r
1736\r
1737/**\r
1738 * Matcher that checks to see if the actual, a Jasmine spy, was not called.\r
1739 *\r
1740 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead\r
1741 */\r
1742jasmine.Matchers.prototype.wasNotCalled = function() {\r
1743 if (arguments.length > 0) {\r
1744 throw new Error('wasNotCalled does not take arguments');\r
1745 }\r
1746\r
1747 if (!jasmine.isSpy(this.actual)) {\r
1748 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1749 }\r
1750\r
1751 this.message = function() {\r
1752 return [\r
1753 "Expected spy " + this.actual.identity + " to not have been called.",\r
1754 "Expected spy " + this.actual.identity + " to have been called."\r
1755 ];\r
1756 };\r
1757\r
1758 return !this.actual.wasCalled;\r
1759};\r
1760\r
1761/**\r
1762 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.\r
1763 *\r
1764 * @example\r
1765 *\r
1766 */\r
1767jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {\r
1768 var expectedArgs = jasmine.util.argsToArray(arguments);\r
1769 if (!jasmine.isSpy(this.actual)) {\r
1770 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1771 }\r
1772 this.message = function() {\r
1773 var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";\r
1774 var positiveMessage = "";\r
1775 if (this.actual.callCount === 0) {\r
1776 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";\r
1777 } else {\r
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, '')\r
1779 }\r
1780 return [positiveMessage, invertedMessage];\r
1781 };\r
1782\r
1783 return this.env.contains_(this.actual.argsForCall, expectedArgs);\r
1784};\r
1785\r
1786/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */\r
1787jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;\r
1788\r
1789/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */\r
1790jasmine.Matchers.prototype.wasNotCalledWith = function() {\r
1791 var expectedArgs = jasmine.util.argsToArray(arguments);\r
1792 if (!jasmine.isSpy(this.actual)) {\r
1793 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1794 }\r
1795\r
1796 this.message = function() {\r
1797 return [\r
1798 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",\r
1799 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"\r
1800 ];\r
1801 };\r
1802\r
1803 return !this.env.contains_(this.actual.argsForCall, expectedArgs);\r
1804};\r
1805\r
1806/**\r
1807 * Matcher that checks that the expected item is an element in the actual Array.\r
1808 *\r
1809 * @param {Object} expected\r
1810 */\r
1811jasmine.Matchers.prototype.toContain = function(expected) {\r
1812 return this.env.contains_(this.actual, expected);\r
1813};\r
1814\r
1815/**\r
1816 * Matcher that checks that the expected item is NOT an element in the actual Array.\r
1817 *\r
1818 * @param {Object} expected\r
1819 * @deprecated as of 1.0. Use not.toContain() instead.\r
1820 */\r
1821jasmine.Matchers.prototype.toNotContain = function(expected) {\r
1822 return !this.env.contains_(this.actual, expected);\r
1823};\r
1824\r
1825jasmine.Matchers.prototype.toBeLessThan = function(expected) {\r
1826 return this.actual < expected;\r
1827};\r
1828\r
1829jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {\r
1830 return this.actual > expected;\r
1831};\r
1832\r
1833/**\r
1834 * Matcher that checks that the expected item is equal to the actual item\r
1835 * up to a given level of decimal precision (default 2).\r
1836 *\r
1837 * @param {Number} expected\r
1838 * @param {Number} precision, as number of decimal places\r
1839 */\r
1840jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {\r
1841 if (!(precision === 0)) {\r
1842 precision = precision || 2;\r
1843 }\r
1844 return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);\r
1845};\r
1846\r
1847/**\r
1848 * Matcher that checks that the expected exception was thrown by the actual.\r
1849 *\r
1850 * @param {String} [expected]\r
1851 */\r
1852jasmine.Matchers.prototype.toThrow = function(expected) {\r
1853 var result = false;\r
1854 var exception;\r
1855 if (typeof this.actual != 'function') {\r
1856 throw new Error('Actual is not a function');\r
1857 }\r
1858 try {\r
1859 this.actual();\r
1860 } catch (e) {\r
1861 exception = e;\r
1862 }\r
1863 if (exception) {\r
1864 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));\r
1865 }\r
1866\r
1867 var not = this.isNot ? "not " : "";\r
1868\r
1869 this.message = function() {\r
1870 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {\r
1871 return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');\r
1872 } else {\r
1873 return "Expected function to throw an exception.";\r
1874 }\r
1875 };\r
1876\r
1877 return result;\r
1878};\r
1879\r
1880jasmine.Matchers.Any = function(expectedClass) {\r
1881 this.expectedClass = expectedClass;\r
1882};\r
1883\r
1884jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {\r
1885 if (this.expectedClass == String) {\r
1886 return typeof other == 'string' || other instanceof String;\r
1887 }\r
1888\r
1889 if (this.expectedClass == Number) {\r
1890 return typeof other == 'number' || other instanceof Number;\r
1891 }\r
1892\r
1893 if (this.expectedClass == Function) {\r
1894 return typeof other == 'function' || other instanceof Function;\r
1895 }\r
1896\r
1897 if (this.expectedClass == Object) {\r
1898 return typeof other == 'object';\r
1899 }\r
1900\r
1901 return other instanceof this.expectedClass;\r
1902};\r
1903\r
1904jasmine.Matchers.Any.prototype.jasmineToString = function() {\r
1905 return '<jasmine.any(' + this.expectedClass + ')>';\r
1906};\r
1907\r
1908jasmine.Matchers.ObjectContaining = function (sample) {\r
1909 this.sample = sample;\r
1910};\r
1911\r
1912jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {\r
1913 mismatchKeys = mismatchKeys || [];\r
1914 mismatchValues = mismatchValues || [];\r
1915\r
1916 var env = jasmine.getEnv();\r
1917\r
1918 var hasKey = function(obj, keyName) {\r
1919 return obj != null && obj[keyName] !== jasmine.undefined;\r
1920 };\r
1921\r
1922 for (var property in this.sample) {\r
1923 if (!hasKey(other, property) && hasKey(this.sample, property)) {\r
1924 mismatchKeys.push("expected has key '" + property + "', but missing from actual.");\r
1925 }\r
1926 else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {\r
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.");\r
1928 }\r
1929 }\r
1930\r
1931 return (mismatchKeys.length === 0 && mismatchValues.length === 0);\r
1932};\r
1933\r
1934jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {\r
1935 return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";\r
1936};\r
1937/**\r
1938 * Override of toThrow special opera 10 !!! \r
1939 */\r
1940jasmine.Matchers.prototype.toThrow = function(expected) {\r
1941 var result = false;\r
1942 var exception;\r
1943 if (typeof this.actual != 'function') {\r
1944 throw new Error('Actual is not a function');\r
1945 }\r
1946\r
1947 // mock the console to avoid logging to the real console during the tests\r
1948 var global = Ext.global;\r
1949\r
1950 Ext.global = {\r
1951 console: {\r
1952 dir: function(s) {\r
1953 return s;\r
1954 },\r
1955 log: function(s) {\r
1956 return s;\r
1957 },\r
1958 error: function(s) {\r
1959 return s;\r
1960 },\r
1961 warn: function(s) {\r
1962 return s;\r
1963 }\r
1964 }\r
1965 };\r
1966\r
1967 // This is to allow setting breakpoints for console messages\r
1968 // that are not expected to be suppressed by jasmine.toThrow and alike\r
1969 Ext.global.console.dir.$emptyFn = Ext.global.console.log.$emptyFn = true;\r
1970 Ext.global.console.error.$emptyFn = Ext.global.console.warn.$emptyFn = true;\r
1971\r
1972 try {\r
1973 this.actual();\r
1974 } catch (e) {\r
1975 exception = e;\r
1976 }\r
1977 if (exception) {\r
1978 result = (expected === jasmine.undefined || this.env.contains_(exception.message || exception, expected.message || expected));\r
1979 }\r
1980\r
1981 Ext.global = global;\r
1982\r
1983 var not = this.isNot ? "not " : "";\r
1984\r
1985 this.message = function() {\r
1986 if (exception && (expected === jasmine.undefined || !this.env.contains_(exception.message || exception, expected.message || expected))) {\r
1987 return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' ');\r
1988 } else {\r
1989 return "Expected function to throw an exception.";\r
1990 }\r
1991 };\r
1992\r
1993 return result;\r
1994};\r
1995\r
1996jasmine.Matchers.prototype.toRaiseExtError = function(expected) {\r
1997 var result = false,\r
1998 global = Ext.global,\r
1999 extError;\r
2000 if (typeof this.actual != 'function') {\r
2001 throw new Error('Actual is not a function');\r
2002 }\r
2003 \r
2004 // mock the console to avoid logging to the real console during the tests\r
2005 Ext.global = {\r
2006 console: {\r
2007 dir: function(s) {\r
2008 return s;\r
2009 },\r
2010 log: function(s) {\r
2011 return s;\r
2012 },\r
2013 error: function(s) {\r
2014 return s;\r
2015 },\r
2016 warn: function(s) {\r
2017 return s;\r
2018 }\r
2019 }\r
2020 };\r
2021 \r
2022 // This is to allow setting breakpoints for console messages\r
2023 // that are not expected to be suppressed by jasmine.toThrow and alike\r
2024 Ext.global.console.dir.$emptyFn = Ext.global.console.log.$emptyFn = true;\r
2025 Ext.global.console.error.$emptyFn = Ext.global.console.warn.$emptyFn = true;\r
2026\r
2027 try {\r
2028 this.actual();\r
2029 } catch (e) {\r
2030 extError = e;\r
2031 }\r
2032\r
2033 \r
2034 Ext.global = global;\r
2035\r
2036 if (extError && extError instanceof Error) {\r
2037 result = (expected === jasmine.undefined || this.env.contains_(extError.toString(), expected.message || expected));\r
2038 }\r
2039\r
2040 \r
2041 var not = this.isNot ? "not " : "";\r
2042\r
2043 this.message = function() {\r
2044 if (!extError instanceof Error) {\r
2045 return "Exception thrown is not an instance of Ext.Error";\r
2046 } else if (extError && (expected === jasmine.undefined || !this.env.contains_(extError.toString(), expected.message || expected))) {\r
2047 return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", extError.toString()].join(' ');\r
2048 } else {\r
2049 return "Expected function to throw an exception.";\r
2050 }\r
2051 };\r
2052\r
2053 return result;\r
2054};\r
2055\r
2056jasmine.Matchers.prototype.hasHTML = function(expected) {\r
2057 var me = this;\r
2058 \r
2059 if (!me.actual || !me.actual.tagName) {\r
2060 throw new Error('Actual is not a dom element');\r
2061 }\r
2062 if (jasmine.browser.isSafari3) {\r
2063 expected = expected.replace(/&gt;/g, '>');\r
2064 }\r
2065 // this normalize innerHTML which could vary a lot\r
2066 var normalizedHTML = me.actual.innerHTML.replace(/<[^>]*>/g, function(match1) {\r
2067 return match1.toLowerCase().replace(/=\w+/g, function(match2) { \r
2068 return '="' + match2.split('=')[1] + '"'; \r
2069 });\r
2070 });\r
2071\r
2072 me.message = function() {\r
2073 return [\r
2074 "Expected dom element innerHTML to be " + expected + " but was " + normalizedHTML,\r
2075 "Expected dom element innerHTML to not be " + expected + "."\r
2076 ];\r
2077 };\r
2078 \r
2079 return normalizedHTML === expected;\r
2080};\r
2081\r
2082jasmine.Matchers.prototype.toHaveCls = function(cls) {\r
2083 return Ext.fly(this.actual).hasCls(cls);\r
2084};\r
2085\r
2086jasmine.Matchers.prototype.toEqualTime = function(hour, minute, second, ms) {\r
2087 var actual = this.actual;\r
2088 return actual instanceof Date &&\r
2089 actual.getHours() === hour &&\r
2090 actual.getMinutes() === (minute || 0) &&\r
2091 actual.getSeconds() === (second || 0) &&\r
2092 actual.getMilliseconds() === (ms || 0);\r
2093\r
2094};\r
2095\r
2096jasmine.Matchers.prototype.toBePositionedAt = function(x, y) {\r
2097 var xy = this.actual.getXY();\r
2098 this.message = function() {\r
2099 return "Expected Ext.Element to be positioned at (" + x + "," + y + ") but was positioned at (" + xy[0] + "," + xy[1] + ")";\r
2100 };\r
2101 return xy[0] === x && xy[1] === y;\r
2102};\r
2103\r
2104(function () {\r
2105 var elementPropGetters = {\r
2106 x: function (el, root) {\r
2107 var x = el.getX(),\r
2108 x0 = root ? root.el.getX() : el.getX();\r
2109 return x - x0;\r
2110 },\r
2111 y: function (el, root) {\r
2112 var y = el.getY(),\r
2113 y0 = root ? root.el.getY() : el.getY();\r
2114 return y - y0;\r
2115 },\r
2116 w: function (el) {\r
2117 return el.getWidth();\r
2118 },\r
2119 h: function (el) {\r
2120 return el.getHeight();\r
2121 },\r
2122 xywh: function(el, root) {\r
2123 var x= el.getX(),\r
2124 x0 = root ? root.el.getX() : el.getX(),\r
2125 y = el.getY(),\r
2126 y0 = root ? root.el.getY() : el.getY(),\r
2127 w = el.getWidth(),\r
2128 h = el.getHeight(),\r
2129 dims = [];\r
2130 dims.push(x - x0, y - y0, w, h);\r
2131 return dims.join(' ');\r
2132 },\r
2133 cls: function (el) {\r
2134 return el.dom.className;\r
2135 }\r
2136 },\r
2137 browsers = [\r
2138 "IE6", "IE7", "IE8", "IE9", "IE",\r
2139 "Gecko3", "Gecko4", "Gecko5", "Gecko10", "Gecko",\r
2140 "FF3_6", "FF4", "FF5",\r
2141 "Chrome",\r
2142 "Safari2", "Safari3", "Safari4", "Safari5", "Safari"\r
2143 ],\r
2144 blen = browsers.length,\r
2145 b, browser,\r
2146 browserCheck = function(expected){\r
2147 if(Ext.isNumeric(expected) || Ext.isArray(expected)) {\r
2148 return expected;\r
2149 }\r
2150 for (b = 0; b < blen; b++) {\r
2151 browser = browsers[b];\r
2152 if (expected.hasOwnProperty(browser) && Ext["is" + browser]){\r
2153 return expected[browser];\r
2154 }\r
2155 }\r
2156 return expected['*'] || expected;\r
2157 },\r
2158 layoutFly = new Ext.dom.Fly();\r
2159\r
2160\r
2161 function checkLayout (comp, layout, root, path) {\r
2162 Ext.Object.each(layout, function (name, value) {\r
2163 if (name == 'items' || name == 'dockedItems') {\r
2164 Ext.Object.each(value, function (id, sub) {\r
2165 var isNum = String(parseInt(id,10)) == id,\r
2166 child = isNum ? comp[name].items[parseInt(id,10)]\r
2167 : (comp.getComponent(id) || comp.child(id));\r
2168\r
2169 if (isNum) {\r
2170 id = '.' + name + '[' + id + ']';\r
2171 } else if (id.charAt(0) != ':') {\r
2172 id = '_' + id;\r
2173 }\r
2174\r
2175 if (child) {\r
2176 checkLayout(child, sub, comp, path + id);\r
2177 } else {\r
2178 expect(id).toBe('found!');\r
2179 }\r
2180 });\r
2181 } else {\r
2182 // the name is an element name like 'body'\r
2183 var el = comp[name];\r
2184\r
2185 if (!el) {\r
2186 // no child el matched, assume the key is a CSS selector\r
2187 el = layoutFly.attach(comp.el.selectNode(name, true));\r
2188 }\r
2189\r
2190 if (el.isComponent) {\r
2191 checkLayout(el, value, el.ownerCt, path + '_' + name);\r
2192 } else if (el.dom) {\r
2193 value = browserCheck(value);\r
2194 if (value.xywh) {\r
2195 var dims = value.xywh.split(' ');\r
2196 value.x = eval('(' + dims[0] + ')');\r
2197 value.y = eval('(' + dims[1] + ')');\r
2198 value.w = eval('(' + dims[2] + ')');\r
2199 value.h = eval('(' + dims[3] + ')');\r
2200 delete value.xywh;\r
2201 }\r
2202 Ext.Object.each(value, function (prop, expected) {\r
2203 var actual = elementPropGetters[prop](el, root || comp.el),\r
2204 pfx = (path ? path + '.' : '') + name + '.' + prop + '=';\r
2205\r
2206 if (Ext.isArray(expected)) {\r
2207 if (actual < expected[0] || actual > expected[1]) {\r
2208 expect(pfx + '=' + actual).\r
2209 toBe('in [' + expected[0] + ',' + expected[1] + ']');\r
2210 }\r
2211 } else if (actual != expected) {\r
2212 expect(pfx + actual).toEqual(expected);\r
2213 }\r
2214 });\r
2215 }\r
2216 }\r
2217 });\r
2218 }\r
2219\r
2220 jasmine.Matchers.prototype.toHaveLayout = function(expected) {\r
2221 var comp = this.actual;\r
2222 checkLayout(comp, expected, comp.ownerCt, comp.getXType());\r
2223 return true;\r
2224 };\r
2225\r
2226 jasmine.Matchers.prototype.toBeLessThanOrEqual = function(expected) {\r
2227 return this.actual <= expected;\r
2228 };\r
2229\r
2230 jasmine.Matchers.prototype.toBeGreaterThanOrEqual = function(expected) {\r
2231 return this.actual >= expected;\r
2232 };\r
2233\r
2234 jasmine.Matchers.prototype.toBeGE = jasmine.Matchers.prototype.toBeAtLeast = jasmine.Matchers.prototype.toBeGreaterThanOrEqual;\r
2235 jasmine.Matchers.prototype.toBeLE = jasmine.Matchers.prototype.toBeLessThanOrEqual;\r
2236 jasmine.Matchers.prototype.toBeLT = jasmine.Matchers.prototype.toBeLessThan;\r
2237 jasmine.Matchers.prototype.toBeGT = jasmine.Matchers.prototype.toBeGreaterThan;\r
2238})();\r
2239\r
2240\r
2241 jasmine.Matchers.prototype.toHaveFiredEvents = function() {\r
2242 var calls = this.actual.fireEvent.calls,\r
2243 i = 0,\r
2244 ret = true,\r
2245 expectedEvents = Array.prototype.slice.call(arguments, 0),\r
2246 length = expectedEvents.length,\r
2247 actualEvents = [], \r
2248 actualEvent,\r
2249 expectedEvent;\r
2250\r
2251\r
2252 \r
2253 for (;i < length; i++) {\r
2254 expectedEvent = expectedEvents[i];\r
2255 try {\r
2256 actualEvent = calls[i].args[0];\r
2257 } catch (e) {\r
2258 actualEvent = null;\r
2259 }\r
2260 if (actualEvent) {\r
2261 actualEvents.push(actualEvent);\r
2262 }\r
2263\r
2264 if (actualEvent != expectedEvent) {\r
2265 ret = false;\r
2266 }\r
2267 }\r
2268 \r
2269 this.message = function() {\r
2270 return "Expected events flow to be (" + expectedEvents.length + " events): \n" + expectedEvents.join('\n') + "\nBut it was (" + actualEvents.length + " events): \n"+ actualEvents.join('\n');\r
2271 };\r
2272 return ret;\r
2273 };\r
2274\r
2275jasmine.Matchers.prototype.toBeApprox = function(expected, errorMargin) {\r
2276 errorMargin = errorMargin || 1;\r
2277 \r
2278 var min = expected - errorMargin,\r
2279 max = expected + errorMargin;\r
2280 \r
2281 this.message = function() {\r
2282 return "Expected " + this.actual + " to be approximately " + expected + " by " + errorMargin;\r
2283 }\r
2284 return this.actual >= min && this.actual <= max;\r
2285};\r
2286\r
2287jasmine.Matchers.prototype.toBeWithin = function(deviation, value) {\r
2288 var actual = this.actual;\r
2289\r
2290 if (deviation > 0) {\r
2291 return actual >= (value - deviation) && actual <= (value + deviation);\r
2292 }\r
2293 else {\r
2294 return actual >= (value + deviation) && actual <= (value - deviation);\r
2295 }\r
2296};\r
2297/**\r
2298 * @constructor\r
2299 */\r
2300jasmine.MultiReporter = function() {\r
2301 this.subReporters_ = [];\r
2302};\r
2303jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);\r
2304\r
2305jasmine.MultiReporter.prototype.addReporter = function(reporter) {\r
2306 this.subReporters_.push(reporter);\r
2307};\r
2308\r
2309(function() {\r
2310 var functionNames = [\r
2311 "reportRunnerStarting",\r
2312 "reportRunnerResults",\r
2313 "reportSuiteStarting",\r
2314 "reportSuiteResults",\r
2315 "reportSpecStarting",\r
2316 "reportSpecResults",\r
2317 "log"\r
2318 ];\r
2319 for (var i = 0; i < functionNames.length; i++) {\r
2320 var functionName = functionNames[i];\r
2321 jasmine.MultiReporter.prototype[functionName] = (function(functionName) {\r
2322 return function() {\r
2323 for (var j = 0; j < this.subReporters_.length; j++) {\r
2324 var subReporter = this.subReporters_[j];\r
2325 if (subReporter[functionName]) {\r
2326 subReporter[functionName].apply(subReporter, arguments);\r
2327 }\r
2328 }\r
2329 };\r
2330 })(functionName);\r
2331 }\r
2332})();/**\r
2333 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults\r
2334 *\r
2335 * @constructor\r
2336 */\r
2337jasmine.NestedResults = function() {\r
2338 /**\r
2339 * The total count of results\r
2340 */\r
2341 this.totalCount = 0;\r
2342 /**\r
2343 * Number of passed results\r
2344 */\r
2345 this.passedCount = 0;\r
2346 /**\r
2347 * Number of failed results\r
2348 */\r
2349 this.failedCount = 0;\r
2350 /**\r
2351 * Was this suite/spec skipped?\r
2352 */\r
2353 this.skipped = false;\r
2354 /**\r
2355 * @ignore\r
2356 */\r
2357 this.items_ = [];\r
2358};\r
2359\r
2360/**\r
2361 * Roll up the result counts.\r
2362 *\r
2363 * @param result\r
2364 */\r
2365jasmine.NestedResults.prototype.rollupCounts = function(result) {\r
2366 this.totalCount += result.totalCount;\r
2367 this.passedCount += result.passedCount;\r
2368 this.failedCount += result.failedCount;\r
2369};\r
2370\r
2371/**\r
2372 * Adds a log message.\r
2373 * @param values Array of message parts which will be concatenated later.\r
2374 */\r
2375jasmine.NestedResults.prototype.log = function(values) {\r
2376 this.items_.push(new jasmine.MessageResult(values));\r
2377};\r
2378\r
2379/**\r
2380 * Getter for the results: message & results.\r
2381 */\r
2382jasmine.NestedResults.prototype.getItems = function() {\r
2383 return this.items_;\r
2384};\r
2385\r
2386/**\r
2387 * Adds a result, tracking counts (total, passed, & failed)\r
2388 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result\r
2389 */\r
2390jasmine.NestedResults.prototype.addResult = function(result) {\r
2391 if (result.type != 'log') {\r
2392 if (result.items_) {\r
2393 this.rollupCounts(result);\r
2394 } else {\r
2395 this.totalCount++;\r
2396 if (result.passed()) {\r
2397 this.passedCount++;\r
2398 } else {\r
2399 this.failedCount++;\r
2400 }\r
2401 }\r
2402 }\r
2403 this.items_.push(result);\r
2404};\r
2405\r
2406/**\r
2407 * @returns {Boolean} True if <b>everything</b> below passed\r
2408 */\r
2409jasmine.NestedResults.prototype.passed = function() {\r
2410 return this.passedCount === this.totalCount;\r
2411};\r
2412\r
2413jasmine.NestedResults.prototype.cleanup = function() {\r
2414 this.items_ = null;\r
2415};\r
2416/**\r
2417 * Base class for pretty printing for expectation results.\r
2418 */\r
2419jasmine.PrettyPrinter = function() {\r
2420 this.ppNestLevel_ = 0;\r
2421};\r
2422\r
2423/**\r
2424 * Formats a value in a nice, human-readable string.\r
2425 *\r
2426 * @param value\r
2427 */\r
2428jasmine.PrettyPrinter.prototype.format = function(value) {\r
2429 this.ppNestLevel_++;\r
2430 try {\r
2431 if (value === jasmine.undefined) {\r
2432 this.emitScalar('undefined');\r
2433 } else if (value === null) {\r
2434 this.emitScalar('null');\r
2435 } else if (value === jasmine.getGlobal()) {\r
2436 this.emitScalar('<global>');\r
2437 } else if (value.jasmineToString) {\r
2438 this.emitScalar(value.jasmineToString());\r
2439 } else if (typeof value === 'string') {\r
2440 this.emitString(value);\r
2441 } else if (jasmine.isSpy(value)) {\r
2442 this.emitScalar("spy on " + value.identity);\r
2443 } else if (value instanceof RegExp) {\r
2444 this.emitScalar(value.toString());\r
2445 } else if (typeof value === 'function') {\r
2446 this.emitScalar('Function');\r
2447 } else if (typeof value.nodeType === 'number') {\r
2448 this.emitScalar('HTMLNode');\r
2449 } else if (value instanceof Date) {\r
2450 this.emitScalar('Date(' + value + ')');\r
2451 } else if (value.__Jasmine_been_here_before__) {\r
2452 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');\r
2453 } else if (jasmine.isArray_(value) || typeof value == 'object') {\r
2454 value.__Jasmine_been_here_before__ = true;\r
2455 if (jasmine.isArray_(value)) {\r
2456 this.emitArray(value);\r
2457 } else {\r
2458 this.emitObject(value);\r
2459 }\r
2460 delete value.__Jasmine_been_here_before__;\r
2461 } else {\r
2462 this.emitScalar(value.toString());\r
2463 }\r
2464 } finally {\r
2465 this.ppNestLevel_--;\r
2466 }\r
2467};\r
2468\r
2469jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {\r
2470 for (var property in obj) {\r
2471 if (!obj.hasOwnProperty(property)) continue;\r
2472 if (property == '__Jasmine_been_here_before__') continue;\r
2473 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && \r
2474 obj.__lookupGetter__(property) !== null) : false);\r
2475 }\r
2476};\r
2477\r
2478jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;\r
2479jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;\r
2480jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;\r
2481jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;\r
2482\r
2483jasmine.StringPrettyPrinter = function() {\r
2484 jasmine.PrettyPrinter.call(this);\r
2485\r
2486 this.string = '';\r
2487};\r
2488jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);\r
2489\r
2490jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {\r
2491 this.append(value);\r
2492};\r
2493\r
2494jasmine.StringPrettyPrinter.prototype.emitString = function(value) {\r
2495 this.append("'" + value + "'");\r
2496};\r
2497\r
2498jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {\r
2499 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
2500 this.append("Array");\r
2501 return;\r
2502 }\r
2503\r
2504 this.append('[ ');\r
2505 for (var i = 0; i < array.length; i++) {\r
2506 if (i > 0) {\r
2507 this.append(', ');\r
2508 }\r
2509 this.format(array[i]);\r
2510 }\r
2511 this.append(' ]');\r
2512};\r
2513\r
2514jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {\r
2515 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
2516 this.append("Object");\r
2517 return;\r
2518 }\r
2519\r
2520 var self = this;\r
2521 this.append('{ ');\r
2522 var first = true;\r
2523\r
2524 this.iterateObject(obj, function(property, isGetter) {\r
2525 if (first) {\r
2526 first = false;\r
2527 } else {\r
2528 self.append(', ');\r
2529 }\r
2530\r
2531 self.append(property);\r
2532 self.append(' : ');\r
2533 if (isGetter) {\r
2534 self.append('<getter>');\r
2535 } else {\r
2536 self.format(obj[property]);\r
2537 }\r
2538 });\r
2539\r
2540 this.append(' }');\r
2541};\r
2542\r
2543jasmine.StringPrettyPrinter.prototype.append = function(value) {\r
2544 this.string += value;\r
2545};\r
2546(function() {\r
2547 var prototype = jasmine.PrettyPrinter.prototype,\r
2548 superFormat = prototype.format;\r
2549\r
2550 prototype.format = function(value) {\r
2551 var className, superclass;\r
2552\r
2553 if (value) {\r
2554 className = value.$className;\r
2555\r
2556 if (className !== undefined) {\r
2557 // support for pretty printing instances of Ext classes\r
2558\r
2559 if (!className) {\r
2560 // support for anonymous classes - Ext.define(null, ...)\r
2561 // loop up the inheritance chain to find nearest non-anonymous ancestor\r
2562 superclass = value.superclass;\r
2563 while (superclass && !superclass.$className) {\r
2564 superclass = superclass.superclass;\r
2565 }\r
2566 if (superclass) {\r
2567 className = superclass.$className;\r
2568 }\r
2569 }\r
2570 this.emitScalar(className + '#' + (value.id || (value.getId && value.getId())));\r
2571 return;\r
2572 }\r
2573 }\r
2574\r
2575 superFormat.call(this, value);\r
2576 };\r
2577})();jasmine.Queue = function(env) {\r
2578 this.env = env;\r
2579\r
2580 // parallel to blocks. each true value in this array means the block will\r
2581 // get executed even if we abort\r
2582 this.ensured = [];\r
2583 this.blocks = [];\r
2584 this.running = false;\r
2585 this.index = 0;\r
2586 this.offset = 0;\r
2587 this.abort = false;\r
2588};\r
2589\r
2590jasmine.Queue.prototype.addBefore = function(block, ensure) {\r
2591 if (ensure === jasmine.undefined) {\r
2592 ensure = false;\r
2593 }\r
2594\r
2595 this.blocks.unshift(block);\r
2596 this.ensured.unshift(ensure);\r
2597};\r
2598\r
2599jasmine.Queue.prototype.add = function(block, ensure) {\r
2600 if (ensure === jasmine.undefined) {\r
2601 ensure = false;\r
2602 }\r
2603\r
2604 this.blocks.push(block);\r
2605 this.ensured.push(ensure);\r
2606};\r
2607\r
2608jasmine.Queue.prototype.insertNext = function(block, ensure) {\r
2609 if (ensure === jasmine.undefined) {\r
2610 ensure = false;\r
2611 }\r
2612\r
2613 this.ensured.splice((this.index + this.offset + 1), 0, ensure);\r
2614 this.blocks.splice((this.index + this.offset + 1), 0, block);\r
2615 this.offset++;\r
2616};\r
2617\r
2618jasmine.Queue.prototype.start = function(onComplete) {\r
2619 this.running = true;\r
2620 this.onComplete = onComplete;\r
2621 this.next_();\r
2622};\r
2623\r
2624jasmine.Queue.prototype.isRunning = function() {\r
2625 return this.running;\r
2626};\r
2627\r
2628jasmine.Queue.LOOP_DONT_RECURSE = true;\r
2629\r
2630jasmine.Queue.prototype.next_ = function() {\r
2631 var self = this;\r
2632 var goAgain = true;\r
2633\r
2634 while (goAgain) {\r
2635 goAgain = false;\r
2636 \r
2637 if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {\r
2638 var calledSynchronously = true;\r
2639 var completedSynchronously = false;\r
2640\r
2641 var onComplete = function () {\r
2642 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {\r
2643 completedSynchronously = true;\r
2644 return;\r
2645 }\r
2646\r
2647 if (self.blocks[self.index].abort) {\r
2648 self.abort = true;\r
2649 }\r
2650\r
2651 self.offset = 0;\r
2652 self.index++;\r
2653\r
2654 var now = new Date().getTime();\r
2655 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {\r
2656 self.env.lastUpdate = now;\r
2657 self.env.setTimeout(function() {\r
2658 self.next_();\r
2659 }, 0);\r
2660 } else {\r
2661 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {\r
2662 goAgain = true;\r
2663 } else {\r
2664 self.next_();\r
2665 }\r
2666 }\r
2667 };\r
2668 self.blocks[self.index].execute(onComplete);\r
2669\r
2670 calledSynchronously = false;\r
2671 if (completedSynchronously) {\r
2672 onComplete();\r
2673 }\r
2674 \r
2675 } else {\r
2676 self.running = false;\r
2677 if (self.onComplete) {\r
2678 self.onComplete();\r
2679 }\r
2680 }\r
2681 }\r
2682};\r
2683\r
2684jasmine.Queue.prototype.results = function() {\r
2685 var results = new jasmine.NestedResults();\r
2686 for (var i = 0; i < this.blocks.length; i++) {\r
2687 if (this.blocks[i].results) {\r
2688 results.addResult(this.blocks[i].results());\r
2689 }\r
2690 }\r
2691 return results;\r
2692};\r
2693\r
2694\r
2695jasmine.Queue.prototype.next_ = function() {\r
2696 var self = this;\r
2697 var goAgain = true;\r
2698\r
2699 while (goAgain) {\r
2700 goAgain = false;\r
2701\r
2702 if (self.index < self.blocks.length) {\r
2703 var calledSynchronously = true;\r
2704 var completedSynchronously = false;\r
2705\r
2706 var onComplete = function() {\r
2707 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {\r
2708 completedSynchronously = true;\r
2709 return;\r
2710 }\r
2711\r
2712 if (self.blocks[self.index].abort) {\r
2713 self.abort = true;\r
2714 }\r
2715\r
2716 self.offset = 0;\r
2717 self.index++;\r
2718\r
2719 var now = new Date().getTime();\r
2720 \r
2721 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {\r
2722 self.env.lastUpdate = now;\r
2723 self.env.setTimeout(function() {\r
2724 self.next_();\r
2725 }, 0);\r
2726 }\r
2727 else {\r
2728 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {\r
2729 goAgain = true;\r
2730 }\r
2731 else {\r
2732 self.next_();\r
2733 }\r
2734 }\r
2735 };\r
2736\r
2737 if (!this.abort || this.ensured[self.index]) {\r
2738 self.blocks[self.index].execute(onComplete);\r
2739 }\r
2740 else {\r
2741 onComplete();\r
2742 }\r
2743\r
2744 calledSynchronously = false;\r
2745 \r
2746 if (completedSynchronously) {\r
2747 onComplete();\r
2748 }\r
2749 }\r
2750 else {\r
2751 self.running = false;\r
2752 \r
2753 if (self.onComplete) {\r
2754 self.onComplete();\r
2755 }\r
2756 \r
2757 self.finish();\r
2758 }\r
2759 }\r
2760};\r
2761\r
2762jasmine.Queue.prototype.finish = function() {\r
2763 var me = this,\r
2764 blocks = me.blocks,\r
2765 i, len, block;\r
2766 \r
2767 // Block functions are closures that keep a lot of test objects retained.\r
2768 // We don't want all this cruft hanging around when the queue is finished.\r
2769 for (i = 0, len = blocks.length; i < len; i++) {\r
2770 block = blocks[i];\r
2771 \r
2772 block.func = null;\r
2773 \r
2774 if (block.latchFunction) {\r
2775 block.latchFunction = null;\r
2776 }\r
2777 \r
2778 if (block.timeout) {\r
2779 block.timeout = null;\r
2780 }\r
2781 }\r
2782 \r
2783 me.finished = true;\r
2784}\r
2785/**\r
2786 * Runner\r
2787 *\r
2788 * @constructor\r
2789 * @param {jasmine.Env} env\r
2790 */\r
2791jasmine.Runner = function(env) {\r
2792 var self = this;\r
2793 self.env = env;\r
2794 self.queue = new jasmine.Queue(env);\r
2795 self.before_ = [];\r
2796 self.after_ = [];\r
2797 self.suites_ = [];\r
2798};\r
2799\r
2800jasmine.Runner.prototype.execute = function() {\r
2801 var self = this;\r
2802 if (self.env.reporter.reportRunnerStarting) {\r
2803 self.env.reporter.reportRunnerStarting(this);\r
2804 }\r
2805 self.queue.start(function () {\r
2806 self.finishCallback();\r
2807 });\r
2808};\r
2809\r
2810jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {\r
2811 beforeEachFunction.typeName = 'beforeEach';\r
2812 this.before_.splice(0,0,beforeEachFunction);\r
2813};\r
2814\r
2815jasmine.Runner.prototype.afterEach = function(afterEachFunction) {\r
2816 afterEachFunction.typeName = 'afterEach';\r
2817 this.after_.splice(0,0,afterEachFunction);\r
2818};\r
2819\r
2820\r
2821jasmine.Runner.prototype.finishCallback = function() {\r
2822 this.env.reporter.reportRunnerResults(this);\r
2823};\r
2824\r
2825jasmine.Runner.prototype.addSuite = function(suite) {\r
2826 this.suites_.push(suite);\r
2827};\r
2828\r
2829jasmine.Runner.prototype.add = function(block) {\r
2830 if (block instanceof jasmine.Suite) {\r
2831 this.addSuite(block);\r
2832 }\r
2833 this.queue.add(block);\r
2834};\r
2835\r
2836jasmine.Runner.prototype.specs = function () {\r
2837 var suites = this.suites();\r
2838 var specs = [];\r
2839 for (var i = 0; i < suites.length; i++) {\r
2840 specs.push.apply(specs, suites[i].specs());\r
2841 }\r
2842 return specs;\r
2843};\r
2844\r
2845jasmine.Runner.prototype.suites = function() {\r
2846 return this.suites_;\r
2847};\r
2848\r
2849jasmine.Runner.prototype.topLevelSuites = function() {\r
2850 var topLevelSuites = [];\r
2851 for (var i = 0; i < this.suites_.length; i++) {\r
2852 if (!this.suites_[i].parentSuite) {\r
2853 topLevelSuites.push(this.suites_[i]);\r
2854 }\r
2855 }\r
2856 return topLevelSuites;\r
2857};\r
2858\r
2859jasmine.Runner.prototype.results = function() {\r
2860 return this.queue.results();\r
2861};\r
2862jasmine.Runner.prototype.filter = function(suiteIds, specIds) {\r
2863 // convert [1, 2] into { 1: true, 2: true }\r
2864 //\r
2865 if (typeof suiteIds.length == 'number') {\r
2866 suiteIds = jasmine.toMap(suiteIds);\r
2867 }\r
2868 if (typeof specIds.length == 'number') {\r
2869 specIds = jasmine.toMap(specIds);\r
2870 }\r
2871\r
2872 var specs = jasmine.getByIds(this.specs(), specIds),\r
2873 suites = jasmine.getByIds(this.suites(), suiteIds),\r
2874 blocks = [],\r
2875 i, length, suite;\r
2876\r
2877 length = specs.length;\r
2878 for (i = 0; i < length; i++) {\r
2879 suite = specs[i].getRootSuite();\r
2880 if (jasmine.array.indexOf(blocks, suite) === -1) {\r
2881 suite.filter(suiteIds, specIds);\r
2882 blocks.push(suite);\r
2883 }\r
2884 }\r
2885\r
2886 length = suites.length;\r
2887 for (i = 0; i < length; i++) {\r
2888 suite = suites[i].getRootSuite();\r
2889 if (jasmine.array.indexOf(blocks, suite) === -1) {\r
2890 suite.filter(suiteIds, specIds);\r
2891 blocks.push(suite);\r
2892 }\r
2893 }\r
2894\r
2895 if (blocks.length) {\r
2896 this.queue.blocks = this.queue.ensured = blocks;\r
2897 \r
2898 // Kill the specs that are never going to run\r
2899 var runningSuites = {};\r
2900 \r
2901 for (i = 0, length = blocks.length; i < length; i++) {\r
2902 suites = blocks[i].allSuites();\r
2903 \r
2904 for (var j = 0, jlen = suites.length; j < jlen; j++) {\r
2905 suite = suites[j];\r
2906 \r
2907 runningSuites[suite.id] = true;\r
2908 }\r
2909 }\r
2910 \r
2911 suites = this.suites();\r
2912 \r
2913 for (i = suites.length - 1; i >= 0; i--) {\r
2914 suite = suites[i];\r
2915 \r
2916 if (!runningSuites[suite.id]) {\r
2917 suites[i] = null;\r
2918 }\r
2919 }\r
2920 }\r
2921 // If suiteIds or specIds are provided, that means the user wanted to run\r
2922 // only the specs or suites specified. If these can't be found, most probably\r
2923 // that means there was a typo in a spec name, or recent changes to the spec code\r
2924 // changed the hash and it's no longer valid. Either way that is something that\r
2925 // happens only when debugging and is going to be corrected soon.\r
2926 // So instead of defaulting to run the whole nine yars, just bail out.\r
2927 else if (specs.length || suites.length) {\r
2928 this.queue.blocks = [];\r
2929 Ext.log.error('No suites or specs found!');\r
2930 }\r
2931 else {\r
2932 blocks = this.queue.blocks;\r
2933 }\r
2934\r
2935 this.env.totalSpecs = 0;\r
2936 for (i = 0; i < blocks.length; ++i) {\r
2937 this.env.totalSpecs += blocks[i].totalSpecs;\r
2938 }\r
2939 this.env.remainingSpecs = this.env.totalSpecs;\r
2940 \r
2941 // We also no longer need hashes\r
2942 jasmine.hashes = null;\r
2943\r
2944 return this;\r
2945};\r
2946/**\r
2947 * Internal representation of a Jasmine specification, or test.\r
2948 *\r
2949 * @constructor\r
2950 * @param {jasmine.Env} env\r
2951 * @param {jasmine.Suite} suite\r
2952 * @param {String} description\r
2953 */\r
2954jasmine.Spec = function(env, suite, description) {\r
2955 if (!env) {\r
2956 throw new Error('jasmine.Env() required');\r
2957 }\r
2958 if (!suite) {\r
2959 throw new Error('jasmine.Suite() required');\r
2960 }\r
2961 var spec = this;\r
2962 spec.id = env.nextSpecId ? env.nextSpecId() : null;\r
2963 spec.env = env;\r
2964 spec.suite = suite;\r
2965 spec.description = description;\r
2966 spec.queue = new jasmine.Queue(env);\r
2967\r
2968 spec.afterCallbacks = [];\r
2969 spec.spies_ = [];\r
2970\r
2971 spec.results_ = new jasmine.NestedResults();\r
2972 spec.results_.description = description;\r
2973 spec.matchersClass = null;\r
2974};\r
2975\r
2976jasmine.Spec.prototype.getFullName = function() {\r
2977 return this.suite.getFullName() + ' ' + this.description + '.';\r
2978};\r
2979\r
2980\r
2981jasmine.Spec.prototype.results = function() {\r
2982 return this.results_;\r
2983};\r
2984\r
2985/**\r
2986 * All parameters are pretty-printed and concatenated together, then written to the spec's output.\r
2987 *\r
2988 * Be careful not to leave calls to <code>jasmine.log</code> in production code.\r
2989 */\r
2990jasmine.Spec.prototype.log = function() {\r
2991 return this.results_.log(arguments);\r
2992};\r
2993\r
2994jasmine.Spec.prototype.runs = function (func) {\r
2995 var block = new jasmine.Block(this.env, func, this);\r
2996 this.addToQueue(block);\r
2997 return this;\r
2998};\r
2999\r
3000jasmine.Spec.prototype.addToQueue = function (block) {\r
3001 if (this.queue.isRunning()) {\r
3002 this.queue.insertNext(block);\r
3003 } else {\r
3004 this.queue.add(block);\r
3005 }\r
3006};\r
3007\r
3008/**\r
3009 * @param {jasmine.ExpectationResult} result\r
3010 */\r
3011jasmine.Spec.prototype.addMatcherResult = function(result) {\r
3012 this.results_.addResult(result);\r
3013};\r
3014\r
3015jasmine.Spec.prototype.expect = function(actual) {\r
3016 var positive = new (this.getMatchersClass_())(this.env, actual, this);\r
3017 positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);\r
3018 return positive;\r
3019};\r
3020\r
3021/**\r
3022 * Waits a fixed time period before moving to the next block.\r
3023 *\r
3024 * @deprecated Use waitsFor() instead\r
3025 * @param {Number} timeout milliseconds to wait\r
3026 */\r
3027jasmine.Spec.prototype.waits = function(timeout) {\r
3028 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);\r
3029 this.addToQueue(waitsFunc);\r
3030 return this;\r
3031};\r
3032\r
3033/**\r
3034 * Waits for the latchFunction to return true before proceeding to the next block.\r
3035 *\r
3036 * @param {Function} latchFunction Function to execute\r
3037 * @param {String} optional_timeoutMessage Message to use if the condition is never met\r
3038 * @param {Number} optional_timeout Time to wait for condition to be met.\r
3039 * @param {Number} optional_timeout_increment Number of milliseconds to wait between invocations.\r
3040 */\r
3041jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout, optional_timeout_increment) {\r
3042 var latchFunction_ = null;\r
3043 var optional_timeoutMessage_ = null;\r
3044 var optional_timeout_ = null;\r
3045 var optional_timeout_increment_ = null;\r
3046 var numberFound = false;\r
3047\r
3048 for (var i = 0; i < arguments.length; i++) {\r
3049 var arg = arguments[i];\r
3050 switch (typeof arg) {\r
3051 case 'function':\r
3052 latchFunction_ = arg;\r
3053 break;\r
3054 case 'string':\r
3055 optional_timeoutMessage_ = arg;\r
3056 break;\r
3057 case 'number':\r
3058 // SECOND number is the increment\r
3059 if (numberFound) {\r
3060 optional_timeout_increment_ = arg;\r
3061 }\r
3062 // FIRST number is the timeout\r
3063 else {\r
3064 optional_timeout_ = arg;\r
3065 numberFound = true;\r
3066 }\r
3067 break;\r
3068 }\r
3069 }\r
3070\r
3071 var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, optional_timeout_increment_, this);\r
3072 this.addToQueue(waitsForFunc);\r
3073 return this;\r
3074};\r
3075\r
3076jasmine.Spec.prototype.fail = function (e) {\r
3077 var expectationResult = new jasmine.ExpectationResult({\r
3078 passed: false,\r
3079 message: e ? jasmine.util.formatException(e) : 'Exception',\r
3080 trace: { stack: e.stack }\r
3081 });\r
3082 this.results_.addResult(expectationResult);\r
3083};\r
3084\r
3085jasmine.Spec.prototype.getMatchersClass_ = function() {\r
3086 return this.matchersClass || this.env.matchersClass;\r
3087};\r
3088\r
3089jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {\r
3090 var parent = this.getMatchersClass_();\r
3091 var newMatchersClass = function() {\r
3092 parent.apply(this, arguments);\r
3093 };\r
3094 jasmine.util.inherit(newMatchersClass, parent);\r
3095 jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);\r
3096 this.matchersClass = newMatchersClass;\r
3097};\r
3098\r
3099jasmine.Spec.prototype.finishCallback = function() {\r
3100 this.env.reporter.reportSpecResults(this);\r
3101};\r
3102\r
3103jasmine.Spec.prototype.finish = function(onComplete) {\r
3104 this.removeAllSpies();\r
3105 this.finishCallback();\r
3106 if (onComplete) {\r
3107 onComplete();\r
3108 }\r
3109 \r
3110 this.finished = true;\r
3111 this.env = this.afterCallbacks_ = this.spies_ = this.spy = this.matchersClass = null;\r
3112};\r
3113\r
3114jasmine.Spec.prototype.after = function(doAfter) {\r
3115 if (this.queue.isRunning()) {\r
3116 this.queue.add(new jasmine.Block(this.env, doAfter, this), true);\r
3117 } else {\r
3118 this.afterCallbacks.unshift(doAfter);\r
3119 }\r
3120};\r
3121\r
3122jasmine.Spec.prototype.execute = function(onComplete) {\r
3123 var spec = this;\r
3124 if (!spec.env.specFilter(spec)) {\r
3125 spec.results_.skipped = true;\r
3126 spec.finish(onComplete);\r
3127 return;\r
3128 }\r
3129\r
3130 this.env.reporter.reportSpecStarting(this);\r
3131\r
3132 spec.env.currentSpec = spec;\r
3133\r
3134 spec.addBeforesAndAftersToQueue();\r
3135\r
3136 spec.queue.start(function () {\r
3137 spec.finish(onComplete);\r
3138 });\r
3139};\r
3140\r
3141jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {\r
3142 var runner = this.env.currentRunner();\r
3143 var i;\r
3144\r
3145 for (var suite = this.suite; suite; suite = suite.parentSuite) {\r
3146 for (i = 0; i < suite.before_.length; i++) {\r
3147 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));\r
3148 }\r
3149 }\r
3150 for (i = 0; i < runner.before_.length; i++) {\r
3151 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));\r
3152 }\r
3153 for (i = 0; i < this.afterCallbacks.length; i++) {\r
3154 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);\r
3155 }\r
3156 for (suite = this.suite; suite; suite = suite.parentSuite) {\r
3157 for (i = 0; i < suite.after_.length; i++) {\r
3158 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);\r
3159 }\r
3160 }\r
3161 for (i = 0; i < runner.after_.length; i++) {\r
3162 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);\r
3163 }\r
3164};\r
3165\r
3166jasmine.Spec.prototype.explodes = function() {\r
3167 throw 'explodes function should not have been called';\r
3168};\r
3169\r
3170jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {\r
3171 if (obj == jasmine.undefined) {\r
3172 throw "spyOn could not find an object to spy upon for " + methodName + "()";\r
3173 }\r
3174\r
3175 if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {\r
3176 throw methodName + '() method does not exist';\r
3177 }\r
3178\r
3179 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {\r
3180 throw new Error(methodName + ' has already been spied upon');\r
3181 }\r
3182\r
3183 var spyObj = jasmine.createSpy(methodName);\r
3184\r
3185 this.spies_.push(spyObj);\r
3186 spyObj.baseObj = obj;\r
3187 spyObj.methodName = methodName;\r
3188 spyObj.originalValue = obj[methodName];\r
3189\r
3190 obj[methodName] = spyObj;\r
3191\r
3192 return spyObj;\r
3193};\r
3194\r
3195jasmine.Spec.prototype.removeAllSpies = function() {\r
3196 for (var i = 0; i < this.spies_.length; i++) {\r
3197 var spy = this.spies_[i];\r
3198 spy.baseObj[spy.methodName] = spy.originalValue;\r
3199 }\r
3200 this.spies_ = [];\r
3201};\r
3202\r
3203(function () {\r
3204 var _Spec = jasmine.Spec,\r
3205 proto = _Spec.prototype,\r
3206 allowedGlobals = {},\r
3207 allowedComponents = {},\r
3208 prop;\r
3209\r
3210 // Any properties already in the window object when we are loading jasmine are ok\r
3211 for (prop in window) {\r
3212 allowedGlobals[prop] = true;\r
3213 }\r
3214\r
3215 // Old Firefox needs these\r
3216 allowedGlobals.getInterface =\r
3217 allowedGlobals.loadFirebugConsole =\r
3218 allowedGlobals._createFirebugConsole =\r
3219 allowedGlobals.netscape =\r
3220 allowedGlobals.XPCSafeJSObjectWrapper =\r
3221 allowedGlobals.XPCNativeWrapper =\r
3222 allowedGlobals.Components =\r
3223 allowedGlobals._firebug =\r
3224 // IE10+ F12 dev tools adds these properties when opened.\r
3225 allowedGlobals.__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE =\r
3226 allowedGlobals.__BROWSERTOOLS_CONSOLE_BREAKMODE_FUNC =\r
3227 allowedGlobals.__BROWSERTOOLS_CONSOLE_SAFEFUNC =\r
3228 // in IE8 jasmine's overrides of setTimeout/setInterval make them iterable\r
3229 allowedGlobals.setTimeout =\r
3230 allowedGlobals.setInterval =\r
3231 allowedGlobals.clearTimeout =\r
3232 allowedGlobals.clearInterval =\r
3233 // we're going to add the addGlobal function to the window object, so specs can call it\r
3234 allowedGlobals.addGlobal =\r
3235 allowedGlobals.id = true; // In Ext JS 4 Ext.get(window) adds an id property\r
3236 \r
3237 if (Ext.toolkit === 'modern') {\r
3238 // Modern MessageBox owns a modal mask component\r
3239 allowedComponents[Ext.Msg.id] = true;\r
3240 allowedComponents[Ext.Msg.getModal().id] = true;\r
3241 }\r
3242 else {\r
3243 // Ext.sparkline.Base puts this tooltip on its prototype\r
3244 allowedComponents['sparklines-tooltip'] = true;\r
3245 }\r
3246 \r
3247 // Ext.MessageBox and its children are going to be present in all tests.\r
3248 // The reason why we don't just add allow everything that is already present\r
3249 // in Ext.ComponentMgr collection is that sometimes components can be created\r
3250 // by mistake in spec definition. We want to catch these as well.\r
3251 (function() {\r
3252 var msgbox = Ext.MessageBox,\r
3253 leaks = [],\r
3254 components = Ext.ComponentMgr.getAll();\r
3255 \r
3256 for (var i = 0; i < components.length; i++) {\r
3257 var cmp = components[i];\r
3258 \r
3259 if (cmp === msgbox || (cmp.up && cmp.up('window') === msgbox)) {\r
3260 allowedComponents[cmp.id] = true;\r
3261 }\r
3262 else if (!allowedComponents[cmp.id]) {\r
3263 leaks.push(cmp);\r
3264 }\r
3265 }\r
3266 \r
3267 if (leaks.length) {\r
3268 Ext.log({\r
3269 dump: leaks,\r
3270 level: 'error',\r
3271 msg: 'COMPONENTS EXIST BEFORE TEST SUITE START'\r
3272 });\r
3273 }\r
3274 \r
3275 Ext.ComponentManager.clearAll();\r
3276 })();\r
3277\r
3278 window.addGlobal = function(property) {\r
3279 var len;\r
3280\r
3281 if (property.charAt) { // string\r
3282 allowedGlobals[property] = true;\r
3283 } else { // array\r
3284 for (len = property.length; len--;) {\r
3285 allowedGlobals[property[len]] = true;\r
3286 }\r
3287 }\r
3288 };\r
3289\r
3290 jasmine.Spec = function () {\r
3291 _Spec.apply(this, arguments);\r
3292 this.fileName = jasmine.getCurrentScript();\r
3293 this.id = jasmine.hashString(this.getFullName(), this.suite.id);\r
3294 this.totalSpecs = 1;\r
3295 };\r
3296\r
3297 jasmine.Spec.prototype = proto;\r
3298\r
3299 // Override: adds the error to the result\r
3300 proto.fail = function (e) {\r
3301 var expectationResult = new jasmine.ExpectationResult({\r
3302 passed: false,\r
3303 message: e ? jasmine.util.formatException(e) : 'Exception'\r
3304 });\r
3305 // Modification start\r
3306 if (e instanceof Error) {\r
3307 expectationResult.error = e;\r
3308 }\r
3309 // Modification end\r
3310 this.results_.addResult(expectationResult);\r
3311 };\r
3312 \r
3313 // Override: check for DOM and global variable leaks\r
3314 proto.finishCallback = function() {\r
3315 if (!jasmine.DISABLE_LEAK_CHECKS) {\r
3316 this.checkDomLeak();\r
3317 this.checkGlobalsLeak();\r
3318 this.checkComponentLeak();\r
3319 }\r
3320 \r
3321 if (Ext.toolkit === 'classic') {\r
3322 this.checkLayoutSuspension();\r
3323 this.checkFocusSuspension();\r
3324 }\r
3325\r
3326 // TODO: this causes too many failures so is disabled for now.\r
3327 // clean up orphan elements and re-enable this at some point.\r
3328 // this.collectGarbage();\r
3329\r
3330 Ext.event.publisher.Gesture.instance.reset();\r
3331\r
3332 this.env.reporter.reportSpecResults(this);\r
3333 \r
3334 // Once the results have been reported, we don't need to keep them anymore;\r
3335 // except when we're running under Cmd. Unlike local reporter, Cmd collects\r
3336 // results in batches per suite so we need to keep the results until the\r
3337 // collection is done. Cmd will then run the suite cleanup.\r
3338 if (!window.Cmd) {\r
3339 this.cleanupResults();\r
3340 }\r
3341 };\r
3342 \r
3343 proto.cleanupResults = function() {\r
3344 var results = this.results();\r
3345 \r
3346 if (results) {\r
3347 results.cleanup();\r
3348 }\r
3349 };\r
3350\r
3351 proto.checkDomLeak = function() {\r
3352 var body = document.body,\r
3353 children = body && body.childNodes || [],\r
3354 len = children.length,\r
3355 badNodes = [],\r
3356 badIds = [],\r
3357 i = 0,\r
3358 child, ids;\r
3359\r
3360 for (; i < len; i++) {\r
3361 child = children[i];\r
3362\r
3363 if (child.nodeType === 3 || !child.getAttribute('data-sticky')) {\r
3364 badIds.push(child.tagName + '#' + child.id);\r
3365 badNodes.push(child);\r
3366 }\r
3367 }\r
3368\r
3369 for (i = 0, len = badNodes.length; i < len; i++) {\r
3370 document.body.removeChild(badNodes[i]);\r
3371 }\r
3372\r
3373 if (badNodes.length) {\r
3374 ids = badIds.join(', ');\r
3375\r
3376 Ext.log({\r
3377 dump: badNodes,\r
3378 level: 'error',\r
3379 msg: 'CLEAN UP YOUR DOM LEAKS IN SPEC: ' + this.getFullName()\r
3380 });\r
3381\r
3382 this.fail('document.body contains childNodes after spec execution --> ' + this.getFullName());\r
3383 }\r
3384 };\r
3385 \r
3386 proto.checkComponentLeak = function(spec) {\r
3387 var leaks = [],\r
3388 components, i, len, cmp;\r
3389 \r
3390 components = Ext.ComponentMgr.getAll();\r
3391 \r
3392 for (i = 0, len = components.length; i < len; i++) {\r
3393 cmp = components[i];\r
3394 \r
3395 // Allow QuickTips by default, they're mostly harmless\r
3396 if (!allowedComponents[cmp.id] &&\r
3397 !(cmp.isQuickTip || (cmp.up && cmp.up('[isQuickTip]')))) {\r
3398 leaks.push(cmp);\r
3399 }\r
3400 }\r
3401 \r
3402 // We don't destroy the leaked components so that they could be examined,\r
3403 // just clearing them from ComponentManager cache is enough\r
3404 Ext.ComponentMgr.clearAll();\r
3405 \r
3406 if (leaks.length) {\r
3407 Ext.log({\r
3408 dump: leaks,\r
3409 level: 'error',\r
3410 msg: 'CLEAN UP YOUR COMPONENT LEAKS IN SPEC: ' + this.getFullName()\r
3411 });\r
3412 \r
3413 this.fail('Ext.ComponentMgr reports undestroyed components after spec execution');\r
3414 }\r
3415 };\r
3416\r
3417 proto.checkGlobalsLeak = function(spec) {\r
3418 var property, value;\r
3419\r
3420 for (property in window) {\r
3421 try {\r
3422 // IE throws error when trying to access window.localStorage\r
3423 value = window[property];\r
3424 } catch(e) {\r
3425 continue;\r
3426 }\r
3427 if (value !== undefined && !allowedGlobals[property] &&\r
3428 (!value || // make sure we don't try to do a property lookup on a null value\r
3429 // old browsers (IE6 and opera 11) add element IDs as enumerable properties\r
3430 // of the window object, so make sure the global var is not a HTMLElement\r
3431 value.nodeType !== 1 &&\r
3432 // make sure it isn't a reference to a window object. This happens in\r
3433 // some browsers (e.g. IE6) when the document contains iframes. The\r
3434 // frames' window objects are referenced by id in the parent window object.\r
3435 !(value.location && value.document))) {\r
3436 this.fail('Bad global variable: ' + property + ' = ' + value);\r
3437 // add the bad global to allowed globals so that it only fails this one spec\r
3438 allowedGlobals[property] = true;\r
3439 }\r
3440 }\r
3441 };\r
3442\r
3443 proto.checkLayoutSuspension = function(spec) {\r
3444 var count = Ext.Component.layoutSuspendCount;\r
3445 if (count !== 0) {\r
3446 this.fail('Spec completed with layouts suspended: count=' + count);\r
3447 Ext.Component.layoutSuspendCount = 0;\r
3448 }\r
3449 };\r
3450 \r
3451 proto.checkFocusSuspension = function(spec) {\r
3452 // If the ExtJS version supports focus suspension...\r
3453 if (Ext.suspendFocus) {\r
3454 var count = Ext.event.publisher.Focus.instance.suspendCount;\r
3455 if (count) {\r
3456 this.fail('Spec completed with focus suspended: count=' + count);\r
3457 Ext.event.publisher.Focus.instance.suspendCount = 0;\r
3458 }\r
3459 }\r
3460 };\r
3461\r
3462 proto.collectGarbage = function() {\r
3463 var ids = Ext.dom.GarbageCollector.collect();\r
3464\r
3465 if (ids.length) {\r
3466 this.fail("Orphan Ext.dom.Element(s) detected: '" + ids.join("', '") + "'");\r
3467 }\r
3468 };\r
3469\r
3470 proto.execute = function(onComplete) {\r
3471 var spec = this;\r
3472 if (!spec.env.specFilter(spec)) {\r
3473 spec.results_.skipped = true;\r
3474 onComplete();\r
3475 return;\r
3476 }\r
3477\r
3478 this.env.reporter.reportSpecStarting(this);\r
3479\r
3480 if (spec.isDisabled()) {\r
3481 spec.results_.skipped = true;\r
3482 spec.finish(onComplete);\r
3483 return;\r
3484 }\r
3485\r
3486 spec.env.currentSpec = spec;\r
3487\r
3488 spec.addBeforesAndAftersToQueue();\r
3489\r
3490 if (spec.debugBlocks && jasmine.getOptions().debug === true) {\r
3491 var blockIdx = jasmine.getOptions().block;\r
3492 if (typeof blockIdx !== 'undefined') {\r
3493 blockIdx = parseInt(blockIdx);\r
3494 var blocks = this.queue.blocks,\r
3495 length = blocks.length,\r
3496 i = 0,\r
3497 block;\r
3498\r
3499 for (; i < length; i++) {\r
3500 block = blocks[i];\r
3501 if (i === blockIdx) {\r
3502 block.func = jasmine.generateDebuggableBlock(block.func);\r
3503 }\r
3504 }\r
3505 }\r
3506 jasmine.showDebugPrompt(function() {\r
3507 spec.queue.start(function () {\r
3508 spec.finish(onComplete);\r
3509 });\r
3510 });\r
3511 } else {\r
3512 spec.queue.start(function () {\r
3513 spec.finish(onComplete);\r
3514 });\r
3515 }\r
3516 };\r
3517\r
3518 proto.enabled = true;\r
3519\r
3520 proto.isEnabled = function() {\r
3521 return this.enabled;\r
3522 };\r
3523\r
3524 proto.isDisabled = function() {\r
3525 return !this.enabled;\r
3526 };\r
3527\r
3528 proto.disable = function() {\r
3529 this.enabled = false;\r
3530 \r
3531 // Release bound contexts and closures\r
3532 this.queue.finish();\r
3533\r
3534 return this;\r
3535 };\r
3536\r
3537 proto.enable = function() {\r
3538 this.enabled = true;\r
3539\r
3540 return this;\r
3541 };\r
3542\r
3543 proto.getRootSuite = function() {\r
3544 var suite = this.suite;\r
3545\r
3546 while (suite.parentSuite) {\r
3547 suite = suite.parentSuite;\r
3548 }\r
3549\r
3550 return suite;\r
3551 };\r
3552})();\r
3553\r
3554/**\r
3555 * Works just like waits() and waitsFor(), except waits for the next animationFrame\r
3556 */\r
3557function waitsForAnimation() {\r
3558 var done = false;\r
3559 runs(function() {\r
3560 Ext.Function.requestAnimationFrame(function() {\r
3561 setTimeout(function() {\r
3562 done = true;\r
3563 }, 1);\r
3564 });\r
3565 });\r
3566 waitsFor(function() {\r
3567 return done;\r
3568 });\r
3569}\r
3570\r
3571/**\r
3572 * Waits for the Spy to have been called before proceeding to the next block.\r
3573 *\r
3574 * @param {Function} spy to wait for\r
3575 * @param {String} [timeoutMessage] Optional timeout message\r
3576 * @param {Number} [timeout] Optional timeout in ms\r
3577 */\r
3578function waitsForSpy(spy, timeoutMessage, timeout) {\r
3579 var currentSpec = jasmine.getEnv().currentSpec;\r
3580 \r
3581 timeoutMessage = timeoutMessage || spy.identity + ' to fire';\r
3582 timeout = timeout != null ? timeout : 1000;\r
3583\r
3584 currentSpec.waitsFor.call(currentSpec, function() { return !!spy.callCount }, timeoutMessage, timeout);\r
3585};\r
3586\r
3587var waitForSpy = waitsForSpy;\r
3588\r
3589/**\r
3590 * Internal representation of a Jasmine suite.\r
3591 *\r
3592 * @constructor\r
3593 * @param {jasmine.Env} env\r
3594 * @param {String} description\r
3595 * @param {Function} specDefinitions\r
3596 * @param {jasmine.Suite} parentSuite\r
3597 */\r
3598jasmine.Suite = function(env, description, specDefinitions, parentSuite) {\r
3599 var self = this;\r
3600 self.id = env.nextSuiteId ? env.nextSuiteId() : null;\r
3601 self.description = description;\r
3602 self.queue = new jasmine.Queue(env);\r
3603 self.parentSuite = parentSuite;\r
3604 self.env = env;\r
3605 self.before_ = [];\r
3606 self.after_ = [];\r
3607 self.children_ = [];\r
3608 self.suites_ = [];\r
3609 self.specs_ = [];\r
3610};\r
3611\r
3612jasmine.Suite.prototype.getFullName = function() {\r
3613 var fullName = this.description;\r
3614 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {\r
3615 fullName = parentSuite.description + ' ' + fullName;\r
3616 }\r
3617 return fullName;\r
3618};\r
3619\r
3620jasmine.Suite.prototype.finish = function(onComplete) {\r
3621 this.env.reporter.reportSuiteResults(this);\r
3622 this.finished = true;\r
3623 if (typeof(onComplete) == 'function') {\r
3624 onComplete();\r
3625 }\r
3626\r
3627 // MUST NOT null the children_ property because that is needed to \r
3628 // traverse the suite's child nodes upon expand and collapse.\r
3629 this.env = this.before_ = this.after_ = null;\r
3630 this.suites_ = this.specs_ = null;\r
3631};\r
3632\r
3633jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {\r
3634 beforeEachFunction.typeName = 'beforeEach';\r
3635 this.before_.unshift(beforeEachFunction);\r
3636};\r
3637\r
3638jasmine.Suite.prototype.afterEach = function(afterEachFunction) {\r
3639 afterEachFunction.typeName = 'afterEach';\r
3640 this.after_.unshift(afterEachFunction);\r
3641};\r
3642\r
3643jasmine.Suite.prototype.results = function() {\r
3644 return this.queue.results();\r
3645};\r
3646\r
3647jasmine.Suite.prototype.add = function(suiteOrSpec) {\r
3648 this.children_.push(suiteOrSpec);\r
3649 if (suiteOrSpec instanceof jasmine.Suite) {\r
3650 this.suites_.push(suiteOrSpec);\r
3651 this.env.currentRunner().addSuite(suiteOrSpec);\r
3652 } else {\r
3653 this.specs_.push(suiteOrSpec);\r
3654 }\r
3655 this.queue.add(suiteOrSpec);\r
3656};\r
3657\r
3658jasmine.Suite.prototype.specs = function() {\r
3659 return this.specs_;\r
3660};\r
3661\r
3662jasmine.Suite.prototype.suites = function() {\r
3663 return this.suites_;\r
3664};\r
3665\r
3666jasmine.Suite.prototype.children = function() {\r
3667 return this.children_;\r
3668};\r
3669\r
3670jasmine.Suite.prototype.execute = function(onComplete) {\r
3671 var self = this;\r
3672 this.queue.start(function () {\r
3673 self.finish(onComplete);\r
3674 });\r
3675};\r
3676(function () {\r
3677 var _Suite = jasmine.Suite,\r
3678 proto = _Suite.prototype;\r
3679\r
3680 jasmine.Suite = function () {\r
3681 _Suite.apply(this, arguments);\r
3682\r
3683 var parentSuite = this.parentSuite;\r
3684 this.totalSpecs = 0;\r
3685 this.fileName = jasmine.getCurrentScript();\r
3686 this.id = jasmine.hashString(this.getFullName(), parentSuite ? parentSuite.id : 0);\r
3687 };\r
3688 \r
3689 jasmine.Suite.prototype = proto;\r
3690 \r
3691 proto.execute = function(onComplete) {\r
3692 var self = this;\r
3693 self.env.reporter.reportSuiteStarting(self); // override\r
3694\r
3695 if (self.isDisabled()) {\r
3696 self.results = self.forceSkippedResults;\r
3697 self.disableChildren();\r
3698 }\r
3699\r
3700 this.queue.start(function () {\r
3701 self.finish(onComplete);\r
3702 });\r
3703 };\r
3704\r
3705 proto.enabled = true;\r
3706\r
3707 proto.isEnabled = function() {\r
3708 return this.enabled;\r
3709 };\r
3710\r
3711 proto.isDisabled = function() {\r
3712 return !this.enabled;\r
3713 };\r
3714\r
3715 proto.adjustCounts = function (amount) {\r
3716 for (var suite = this; suite; suite = suite.parentSuite) {\r
3717 suite.totalSpecs += amount;\r
3718 }\r
3719 };\r
3720\r
3721 proto.disable = function() {\r
3722 this.enabled = false;\r
3723 return this;\r
3724 };\r
3725\r
3726 proto.enable = function() {\r
3727 this.enabled = true;\r
3728 return this;\r
3729 };\r
3730\r
3731 proto.forceSkippedResults = function() {\r
3732 var results = this.queue.results();\r
3733 results.skipped = true;\r
3734 \r
3735 return results;\r
3736 };\r
3737\r
3738 proto.disableChildren = function() {\r
3739 var children = this.children(),\r
3740 length = children.length,\r
3741 i = 0;\r
3742\r
3743 for (; i < length; i++) {\r
3744 children[i].disable();\r
3745 }\r
3746\r
3747 return this;\r
3748 };\r
3749\r
3750 proto.filter = function (suiteIds, specIds) {\r
3751 if (!suiteIds[this.id]) {\r
3752 var specs = this.specs(),\r
3753 suites = this.suites(),\r
3754 spec, i, suite, length;\r
3755\r
3756 length = specs.length;\r
3757\r
3758 for (i = 0; i < length; i++) {\r
3759 spec = specs[i];\r
3760 if (!specIds[spec.id]) {\r
3761 jasmine.array.remove(this.queue.blocks, spec);\r
3762 this.adjustCounts(-spec.totalSpecs);\r
3763 }\r
3764 }\r
3765\r
3766 length = suites.length;\r
3767\r
3768 for (i = 0; i < length; i++) {\r
3769 suite = suites[i];\r
3770 suite.filter(suiteIds, specIds);\r
3771 if (suite.empty) {\r
3772 jasmine.array.remove(this.queue.blocks, suite);\r
3773 this.adjustCounts(-suite.totalSpecs);\r
3774 }\r
3775 }\r
3776\r
3777 if (this.queue.blocks.length === 0) {\r
3778 this.empty = true;\r
3779 }\r
3780 }\r
3781\r
3782 return this;\r
3783 };\r
3784\r
3785 proto.getRootSuite = function() {\r
3786 var suite = this;\r
3787\r
3788 while (suite.parentSuite) {\r
3789 suite = suite.parentSuite;\r
3790 }\r
3791\r
3792 return suite;\r
3793 };\r
3794\r
3795 proto.add = function(suiteOrSpec) {\r
3796 this.children_.push(suiteOrSpec);\r
3797 if (suiteOrSpec instanceof jasmine.Suite) {\r
3798 this.suites_.push(suiteOrSpec);\r
3799 this.env.currentRunner().addSuite(suiteOrSpec);\r
3800 } else {\r
3801 this.specs_.push(suiteOrSpec);\r
3802 }\r
3803 this.queue.add(suiteOrSpec);\r
3804\r
3805 for (var p = this; p; p = p.parentSuite) {\r
3806 p.totalSpecs += suiteOrSpec.totalSpecs;\r
3807 }\r
3808 };\r
3809 \r
3810 proto.allSuites = function() {\r
3811 var all = [],\r
3812 suites, subSuites, i, len;\r
3813 \r
3814 suites = this.suites();\r
3815 \r
3816 for (i = 0, len = suites.length; i < len; i++) {\r
3817 all.push(suites[i]);\r
3818 \r
3819 subSuites = suites[i].allSuites();\r
3820 \r
3821 if (subSuites.length) {\r
3822 all.push(subSuites);\r
3823 }\r
3824 }\r
3825 \r
3826 return all.length ? Ext.Array.flatten(all) : all;\r
3827 };\r
3828 \r
3829 proto.cleanupResults = function() {\r
3830 var results = this.results();\r
3831 \r
3832 if (results) {\r
3833 results.cleanup();\r
3834 }\r
3835 };\r
3836})();\r
3837jasmine.WaitsBlock = function(env, timeout, spec) {\r
3838 this.timeout = timeout;\r
3839 jasmine.Block.call(this, env, null, spec);\r
3840};\r
3841\r
3842jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);\r
3843\r
3844jasmine.WaitsBlock.prototype.execute = function (onComplete) {\r
3845 if (jasmine.VERBOSE) {\r
3846 this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');\r
3847 }\r
3848 this.env.setTimeout(function () {\r
3849 onComplete();\r
3850 }, this.timeout);\r
3851};\r
3852/**\r
3853 * A block which waits for some condition to become true, with timeout.\r
3854 *\r
3855 * @constructor\r
3856 * @extends jasmine.Block\r
3857 * @param {jasmine.Env} env The Jasmine environment.\r
3858 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.\r
3859 * @param {Function} latchFunction A function which returns true when the desired condition has been met.\r
3860 * @param {String} message The message to display if the desired condition hasn't been met within the given time period.\r
3861 * @param {NUmber} timeout_increment Time in milliseconds to wait between invocations.\r
3862 * @param {jasmine.Spec} spec The Jasmine spec.\r
3863 */\r
3864jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, timeout_increment, spec) {\r
3865 this.timeout = timeout || env.defaultTimeoutInterval;\r
3866 this.latchFunction = latchFunction;\r
3867 this.message = message;\r
3868 this.totalTimeSpentWaitingForLatch = 0;\r
3869 this.timeout_increment = timeout_increment || jasmine.WaitsForBlock.TIMEOUT_INCREMENT;\r
3870 jasmine.Block.call(this, env, null, spec);\r
3871};\r
3872jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);\r
3873\r
3874jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;\r
3875\r
3876jasmine.WaitsForBlock.prototype.execute = function(onComplete) {\r
3877 if (jasmine.VERBOSE) {\r
3878 this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));\r
3879 }\r
3880 var latchFunctionResult;\r
3881 try {\r
3882 latchFunctionResult = this.latchFunction.call(this.spec, this.timeout, this.totalTimeSpentWaitingForLatch);\r
3883 } catch (e) {\r
3884 this.spec.fail(e);\r
3885 onComplete();\r
3886 return;\r
3887 }\r
3888\r
3889 if (latchFunctionResult) {\r
3890 onComplete();\r
3891 } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {\r
3892 var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');\r
3893 this.spec.fail({\r
3894 name: 'timeout',\r
3895 message: message\r
3896 });\r
3897\r
3898 this.abort = true;\r
3899 onComplete();\r
3900 } else {\r
3901 this.totalTimeSpentWaitingForLatch += this.timeout_increment;\r
3902 var self = this;\r
3903 this.env.setTimeout(function() {\r
3904 self.execute(onComplete);\r
3905 }, this.timeout_increment);\r
3906 }\r
3907};\r
3908// Mock setTimeout, clearTimeout\r
3909// Contributed by Pivotal Computer Systems, www.pivotalsf.com\r
3910\r
3911jasmine.FakeTimer = function() {\r
3912 this.reset();\r
3913\r
3914 var self = this;\r
3915 self.setTimeout = function(funcToCall, millis) {\r
3916 self.timeoutsMade++;\r
3917 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);\r
3918 return self.timeoutsMade;\r
3919 };\r
3920\r
3921 self.setInterval = function(funcToCall, millis) {\r
3922 self.timeoutsMade++;\r
3923 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);\r
3924 return self.timeoutsMade;\r
3925 };\r
3926\r
3927 self.clearTimeout = function(timeoutKey) {\r
3928 self.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3929 };\r
3930\r
3931 self.clearInterval = function(timeoutKey) {\r
3932 self.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3933 };\r
3934\r
3935};\r
3936\r
3937jasmine.FakeTimer.prototype.reset = function() {\r
3938 this.timeoutsMade = 0;\r
3939 this.scheduledFunctions = {};\r
3940 this.nowMillis = 0;\r
3941};\r
3942\r
3943jasmine.FakeTimer.prototype.tick = function(millis) {\r
3944 var oldMillis = this.nowMillis;\r
3945 var newMillis = oldMillis + millis;\r
3946 this.runFunctionsWithinRange(oldMillis, newMillis);\r
3947 this.nowMillis = newMillis;\r
3948};\r
3949\r
3950jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {\r
3951 var scheduledFunc;\r
3952 var funcsToRun = [];\r
3953 for (var timeoutKey in this.scheduledFunctions) {\r
3954 scheduledFunc = this.scheduledFunctions[timeoutKey];\r
3955 if (scheduledFunc != jasmine.undefined &&\r
3956 scheduledFunc.runAtMillis >= oldMillis &&\r
3957 scheduledFunc.runAtMillis <= nowMillis) {\r
3958 funcsToRun.push(scheduledFunc);\r
3959 this.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3960 }\r
3961 }\r
3962\r
3963 if (funcsToRun.length > 0) {\r
3964 funcsToRun.sort(function(a, b) {\r
3965 return a.runAtMillis - b.runAtMillis;\r
3966 });\r
3967 for (var i = 0; i < funcsToRun.length; ++i) {\r
3968 try {\r
3969 var funcToRun = funcsToRun[i];\r
3970 this.nowMillis = funcToRun.runAtMillis;\r
3971 funcToRun.funcToCall();\r
3972 if (funcToRun.recurring) {\r
3973 this.scheduleFunction(funcToRun.timeoutKey,\r
3974 funcToRun.funcToCall,\r
3975 funcToRun.millis,\r
3976 true);\r
3977 }\r
3978 } catch(e) {\r
3979 }\r
3980 }\r
3981 this.runFunctionsWithinRange(oldMillis, nowMillis);\r
3982 }\r
3983};\r
3984\r
3985jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {\r
3986 this.scheduledFunctions[timeoutKey] = {\r
3987 runAtMillis: this.nowMillis + millis,\r
3988 funcToCall: funcToCall,\r
3989 recurring: recurring,\r
3990 timeoutKey: timeoutKey,\r
3991 millis: millis\r
3992 };\r
3993};\r
3994\r
3995/**\r
3996 * @namespace\r
3997 */\r
3998jasmine.Clock = {\r
3999 defaultFakeTimer: new jasmine.FakeTimer(),\r
4000\r
4001 reset: function() {\r
4002 jasmine.Clock.assertInstalled();\r
4003 jasmine.Clock.defaultFakeTimer.reset();\r
4004 },\r
4005\r
4006 tick: function(millis) {\r
4007 jasmine.Clock.assertInstalled();\r
4008 jasmine.Clock.defaultFakeTimer.tick(millis);\r
4009 },\r
4010\r
4011 runFunctionsWithinRange: function(oldMillis, nowMillis) {\r
4012 jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);\r
4013 },\r
4014\r
4015 scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {\r
4016 jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);\r
4017 },\r
4018\r
4019 useMock: function() {\r
4020 if (!jasmine.Clock.isInstalled()) {\r
4021 var spec = jasmine.getEnv().currentSpec;\r
4022 spec.after(jasmine.Clock.uninstallMock);\r
4023\r
4024 jasmine.Clock.installMock();\r
4025 }\r
4026 },\r
4027\r
4028 installMock: function() {\r
4029 jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;\r
4030 },\r
4031\r
4032 uninstallMock: function() {\r
4033 jasmine.Clock.assertInstalled();\r
4034 jasmine.Clock.installed = jasmine.Clock.real;\r
4035 },\r
4036\r
4037 real: {\r
4038 setTimeout: jasmine.getGlobal().setTimeout,\r
4039 clearTimeout: jasmine.getGlobal().clearTimeout,\r
4040 setInterval: jasmine.getGlobal().setInterval,\r
4041 clearInterval: jasmine.getGlobal().clearInterval\r
4042 },\r
4043\r
4044 assertInstalled: function() {\r
4045 if (!jasmine.Clock.isInstalled()) {\r
4046 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");\r
4047 }\r
4048 },\r
4049\r
4050 isInstalled: function() {\r
4051 return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;\r
4052 },\r
4053\r
4054 installed: null\r
4055};\r
4056jasmine.Clock.installed = jasmine.Clock.real;\r
4057\r
4058//else for IE support\r
4059jasmine.getGlobal().setTimeout = function(funcToCall, millis) {\r
4060 if (jasmine.Clock.installed.setTimeout.apply) {\r
4061 return jasmine.Clock.installed.setTimeout.apply(this, arguments);\r
4062 } else {\r
4063 return jasmine.Clock.installed.setTimeout(funcToCall, millis);\r
4064 }\r
4065};\r
4066\r
4067jasmine.getGlobal().setInterval = function(funcToCall, millis) {\r
4068 if (jasmine.Clock.installed.setInterval.apply) {\r
4069 return jasmine.Clock.installed.setInterval.apply(this, arguments);\r
4070 } else {\r
4071 return jasmine.Clock.installed.setInterval(funcToCall, millis);\r
4072 }\r
4073};\r
4074\r
4075jasmine.getGlobal().clearTimeout = function(timeoutKey) {\r
4076 if (jasmine.Clock.installed.clearTimeout.apply) {\r
4077 return jasmine.Clock.installed.clearTimeout.apply(this, arguments);\r
4078 } else {\r
4079 return jasmine.Clock.installed.clearTimeout(timeoutKey);\r
4080 }\r
4081};\r
4082\r
4083jasmine.getGlobal().clearInterval = function(timeoutKey) {\r
4084 if (jasmine.Clock.installed.clearTimeout.apply) {\r
4085 return jasmine.Clock.installed.clearInterval.apply(this, arguments);\r
4086 } else {\r
4087 return jasmine.Clock.installed.clearInterval(timeoutKey);\r
4088 }\r
4089};\r
4090\r
4091jasmine.mouseToPointerMap = {\r
4092 mousedown: 'pointerdown',\r
4093 mousemove: 'pointermove',\r
4094 mouseup: 'pointerup',\r
4095 mouseover: 'pointerover',\r
4096 mouseout: 'pointerout',\r
4097 mouseenter: 'pointerenter',\r
4098 mouseleave: 'pointerleave'\r
4099};\r
4100\r
4101jasmine.pointerEventsMap = Ext.supports.MSPointerEvents && !Ext.supports.PointerEvents ? {\r
4102 // translation map for IE10\r
4103 pointerdown: 'MSPointerDown',\r
4104 pointermove: 'MSPointerMove',\r
4105 pointerup: 'MSPointerUp',\r
4106 pointerover: 'MSPointerOver',\r
4107 pointerout: 'MSPointerOut',\r
4108 // IE10 does not have pointer events for enter/leave\r
4109 pointerenter: 'mouseenter',\r
4110 pointerleave: 'mouseleave'\r
4111} : {};\r
4112\r
4113/**\r
4114 * Utility function to fire a fake mouse event to a given target element\r
4115 */\r
4116jasmine.fireMouseEvent = function (target, type, x, y, button, shiftKey, ctrlKey, altKey) {\r
4117 var e, doc, docEl, body, ret, pointerEventType;\r
4118\r
4119 target = Ext.getDom(target && target.isComponent ? target.el : target);\r
4120 if (!target) {\r
4121 throw 'Cannot fire mouse event on null element';\r
4122 }\r
4123 doc = target.ownerDocument || document;\r
4124 x = x || 0;\r
4125 y = y || 0;\r
4126\r
4127 if (Ext.isIE9m && doc.createEventObject){ // IE event model\r
4128 e = doc.createEventObject();\r
4129 docEl = doc.documentElement;\r
4130 body = doc.body;\r
4131 x = x + (docEl && docEl.clientLeft || 0) + (body && body.clientLeft || 0);\r
4132 y = y + (docEl && docEl.clientTop || 0) + (body && body.clientLeft || 0);\r
4133 Ext.apply(e, {\r
4134 bubbles: true,\r
4135 cancelable: true,\r
4136 screenX: x,\r
4137 screenY: y,\r
4138 clientX: x,\r
4139 clientY: y,\r
4140 button: button || 1,\r
4141 shiftKey: !!shiftKey,\r
4142 ctrlKey: !!ctrlKey,\r
4143 altKey: !!altKey\r
4144 });\r
4145 if (type === 'click') {\r
4146 target.fireEvent('onmousedown', e);\r
4147 target.fireEvent('onmouseup', e);\r
4148 } else if (type === 'dblclick') {\r
4149 jasmine.fireMouseEvent(target, 'click', x, y, button, shiftKey, ctrlKey, altKey);\r
4150 target.fireEvent('onmousedown', e);\r
4151 target.fireEvent('onmouseup', e);\r
4152 }\r
4153 ret = target.fireEvent('on' + type, e);\r
4154 } else {\r
4155 if (Ext.supports.PointerEvents || Ext.supports.MSPointerEvents) {\r
4156 // In IE10 and higher the framework translates mouse event listeners to pointer\r
4157 // event listeners by default. This means that if we fire only mouse events, our\r
4158 // pointer event listeners will not be fired. To fix this, we have to emulate\r
4159 // what the browser does when the mouse is clicked or screen is touched - fire\r
4160 // a pointer event followed by a compatibility mouse event.\r
4161 // see http://www.w3.org/TR/pointerevents/#dfn-compatibility-mouse-events\r
4162 if (type === 'click') {\r
4163 // In IE10+ the framework translates click to tap, which means we must\r
4164 // fire the events from which tap is sythesized (pointerdown/pointerup)\r
4165 // if we want our listeners to run.\r
4166 jasmine.firePointerEvent(target, 'pointerdown', 1, x, y, button, shiftKey, ctrlKey, altKey);\r
4167 jasmine.doFireMouseEvent(target, 'mousedown', x, y, button, shiftKey, ctrlKey, altKey);\r
4168 jasmine.firePointerEvent(target, 'pointerup', 1, x, y, button, shiftKey, ctrlKey, altKey);\r
4169 jasmine.doFireMouseEvent(target, 'mouseup', x, y, button, shiftKey, ctrlKey, altKey);\r
4170 } else if (type === 'dblclick') {\r
4171 // click (which triggers its own (pointerdown/mousdown/pointerup/mouseup\r
4172 // sequence) followed by a second pointerdown/mousedown/pointerup/mouseup\r
4173 // sequence always precedes dblclick\r
4174 jasmine.fireMouseEvent(target, 'click', x, y, button, shiftKey, ctrlKey, altKey);\r
4175 jasmine.firePointerEvent(target, 'pointerdown', 1, x, y, button, shiftKey, ctrlKey, altKey);\r
4176 jasmine.doFireMouseEvent(target, 'mousedown', x, y, button, shiftKey, ctrlKey, altKey);\r
4177 jasmine.firePointerEvent(target, 'pointerup', 1, x, y, button, shiftKey, ctrlKey, altKey);\r
4178 jasmine.doFireMouseEvent(target, 'mouseup', x, y, button, shiftKey, ctrlKey, altKey);\r
4179 } else {\r
4180 // plain old mouse event (mousedown, mousemove, etc.) - fire the corresponding\r
4181 // pointer event before dispatching the mouse event\r
4182 pointerEventType = jasmine.mouseToPointerMap[type];\r
4183 if (pointerEventType) {\r
4184 jasmine.firePointerEvent(target, pointerEventType, 1, x, y, button);\r
4185 }\r
4186 }\r
4187 } else if (type === 'click') {\r
4188 // simulate a mousedown/mouseup sequence before firing a click event\r
4189 jasmine.fireMouseEvent(target, 'mousedown', x, y, button, shiftKey, ctrlKey, altKey);\r
4190 jasmine.fireMouseEvent(target, 'mouseup', x, y, button, shiftKey, ctrlKey, altKey);\r
4191 } else if (type === 'dblclick') {\r
4192 // click (which includes its own mousedown/mouseup sequence) followed by a second\r
4193 // mousedown/mouseup always precedes dblclick\r
4194 jasmine.fireMouseEvent(target, 'click', x, y, button, shiftKey, ctrlKey, altKey);\r
4195 jasmine.fireMouseEvent(target, 'mousedown', x, y, button, shiftKey, ctrlKey, altKey);\r
4196 jasmine.fireMouseEvent(target, 'mouseup', x, y, button, shiftKey, ctrlKey, altKey);\r
4197 }\r
4198\r
4199 ret = jasmine.doFireMouseEvent(target, type, x, y, button, shiftKey, ctrlKey, altKey);\r
4200 }\r
4201\r
4202 return (ret === false) ? ret : e;\r
4203};\r
4204\r
4205jasmine.doFireMouseEvent = function(target, type, x, y, button, shiftKey, ctrlKey, altKey) {\r
4206 var doc = target.ownerDocument || document,\r
4207 e = doc.createEvent("MouseEvents");\r
4208\r
4209 e.initMouseEvent(type, true, true, doc.defaultView || doc.parentWindow, 1, x, y, x, y, !!ctrlKey, !!altKey, !!shiftKey, false, button || 0, null);\r
4210 return target.dispatchEvent(e);\r
4211};\r
4212\r
4213/**\r
4214 * Fires a pointer event. Since PointerEvents cannot yet be directly constructed,\r
4215 * we fake it by constructing a mouse event and setting its pointer id. This method\r
4216 * should typically be used when (Ext.supports.PointerEvents || Ext.supports.MSPointerEvents).\r
4217 * @param {String/Ext.Element/HTMLElement} target\r
4218 * @param {String} type The name of the event to fire\r
4219 * @param {Number} pointerId A unique id for the pointer, for more on pointerId see\r
4220 * http://www.w3.org/TR/pointerevents/\r
4221 * @param {Number} x The x coordinate\r
4222 * @param {Number} y The y coordinate\r
4223 * @param {Number} button\r
4224 * @return {Boolean} true if the event was successfully dispatched\r
4225 */\r
4226jasmine.firePointerEvent = function(target, type, pointerId, x, y, button, shiftKey, ctrlKey, altKey) {\r
4227 var doc = document,\r
4228 e = doc.createEvent("MouseEvents"),\r
4229 target = Ext.getDom(target),\r
4230 dispatched;\r
4231\r
4232 if (!target) {\r
4233 throw 'Cannot fire pointer event on null element';\r
4234 }\r
4235\r
4236 type = jasmine.pointerEventsMap[type] || type;\r
4237\r
4238 e.initMouseEvent(\r
4239 type, // type\r
4240 true, // canBubble\r
4241 true, // cancelable\r
4242 doc.defaultView || doc.parentWindow, // view\r
4243 1, // detail\r
4244 x, // screenX\r
4245 y, // screenY\r
4246 x, // clientX\r
4247 y, // clientY\r
4248 !!ctrlKey, // ctrlKey\r
4249 !!altKey, // altKey\r
4250 !!shiftKey, // shiftKey\r
4251 false, // metaKey\r
4252 button || 0, // button\r
4253 null // relatedTarget\r
4254 );\r
4255 e.pointerId = pointerId || 1;\r
4256 e.pointerType = 'mouse';\r
4257\r
4258 dispatched = target.dispatchEvent(e);\r
4259\r
4260 return (dispatched === false) ? dispatched : e;\r
4261};\r
4262\r
4263jasmine.createTouchList = function(touchList, target) {\r
4264 var doc = document,\r
4265 i = 0,\r
4266 ln = touchList.length,\r
4267 touches = [],\r
4268 touchCfg;\r
4269\r
4270 for (; i < ln; i++) {\r
4271 touchCfg = touchList[i];\r
4272 touches.push(doc.createTouch(\r
4273 doc.defaultView || doc.parentWindow,\r
4274 touchCfg.target || target,\r
4275 // use 1 as the default ID, so that tests that are only concerned with a single\r
4276 // touch event don't need to worry about providing an ID\r
4277 touchCfg.identifier || 1,\r
4278 touchCfg.pageX,\r
4279 touchCfg.pageY,\r
4280 touchCfg.screenX || touchCfg.pageX, // use pageX/Y as the default for screenXY\r
4281 touchCfg.screenY || touchCfg.pageY\r
4282 ));\r
4283 }\r
4284\r
4285 return doc.createTouchList.apply(doc, touches);\r
4286};\r
4287\r
4288/**\r
4289 * Utility for emulating a touch event. This method should typically only be used when\r
4290 * Ext.supports.TouchEvents. Recommended reading for understanding how touch events work:\r
4291 * http://www.w3.org/TR/touch-events/\r
4292 * @param {String/Ext.Element/HTMLElement} target\r
4293 * @param {String} type The name of the event to fire\r
4294 * @param {Object[]} touches An array of config objects for constructing the event object's\r
4295 * "touches". The config objects conform to the following interface:\r
4296 * http://www.w3.org/TR/touch-events/#idl-def-Touch The only required properties\r
4297 * are pageX and pageY. this method provides defaults for the others.\r
4298 * @param {Object[]} changedTouches An array of config objects for constructing the event\r
4299 * object's "changedTouches" (defaults to the same value as the `touches` param)\r
4300 * @param {Object[]} targetTouches An array of config objects for constructing the event\r
4301 * object's "targetTouches" (defaults to the same value as the `touches` param)\r
4302 * @param {Number} scale\r
4303 * @param {Number} rotation\r
4304 * @return {Boolean} true if the event was successfully dispatched\r
4305 */\r
4306jasmine.fireTouchEvent = function(target, type, touches, changedTouches, targetTouches) {\r
4307 var doc = document,\r
4308 // Couldn't figure out how to set touches, changedTouches targetTouches on a "real"\r
4309 // TouchEvent, initTouchEvent seems to ignore the parameters documented here:\r
4310 // https://developer.apple.com/library/safari/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.htmlTouchLists\r
4311 // Apparently directly assigning to e.touches after creating a TouchEvent doesn't\r
4312 // work either so the best we can do is just make a CustomEvent and fake it.\r
4313 e = new CustomEvent(type, {\r
4314 bubbles: true,\r
4315 cancelable: true\r
4316 }),\r
4317 target = Ext.getDom(target),\r
4318 dispatched;\r
4319\r
4320 if (!target) {\r
4321 throw 'Cannot fire touch event on null element';\r
4322 }\r
4323\r
4324 Ext.apply(e, {\r
4325 target: target,\r
4326 touches: jasmine.createTouchList(touches, target),\r
4327 changedTouches: jasmine.createTouchList(changedTouches ? changedTouches : touches, target),\r
4328 targetTouches: jasmine.createTouchList(targetTouches ? targetTouches : touches, target)\r
4329 });\r
4330\r
4331 dispatched = target.dispatchEvent(e);\r
4332\r
4333 return (dispatched === false) ? dispatched : e;\r
4334};\r
4335\r
4336/**\r
4337 * Utility function to fire a fake key event to a given target element\r
4338 */\r
4339jasmine.fireKeyEvent = function(target, type, key, shiftKey, ctrlKey, altKey) {\r
4340 var e,\r
4341 doc;\r
4342 target = Ext.getDom(target);\r
4343 if (!target) {\r
4344 throw 'Cannot fire key event on null element';\r
4345 }\r
4346 doc = target.ownerDocument || document;\r
4347 if (Ext.isIE9m && doc.createEventObject) { //IE event model\r
4348 e = doc.createEventObject();\r
4349 Ext.apply(e, {\r
4350 bubbles: true,\r
4351 cancelable: true,\r
4352 keyCode: key,\r
4353 shiftKey: !!shiftKey,\r
4354 ctrlKey: !!ctrlKey,\r
4355 altKey: !!altKey\r
4356 });\r
4357 return target.fireEvent('on' + type, e);\r
4358 } else {\r
4359 e = doc.createEvent("Events");\r
4360 e.initEvent(type, true, true);\r
4361 Ext.apply(e, {\r
4362 keyCode: key,\r
4363 shiftKey: !!shiftKey,\r
4364 ctrlKey: !!ctrlKey,\r
4365 altKey: !!altKey\r
4366 });\r
4367 return target.dispatchEvent(e);\r
4368 }\r
4369};\r
4370\r
4371// This implementation is pretty naïve but since it's not easy to simulate\r
4372// real Tab key presses (if at all possible), it doesn't make sense to go\r
4373// any further than this.\r
4374jasmine.simulateTabKey = jasmine.syncPressTabKey = function(from, forward) {\r
4375 function getNextTabTarget(currentlyFocused, forward) {\r
4376 var selector = 'a[href],button,iframe,input,select,textarea,[tabindex],[contenteditable="true"]',\r
4377 body = Ext.getBody(),\r
4378 currentDom, focusables, node, next,\r
4379 len, lastIdx, currIdx, i, to, step;\r
4380 \r
4381 currentDom = Ext.getDom(currentlyFocused);\r
4382 focusables = body.dom.querySelectorAll(selector);\r
4383 len = focusables.length;\r
4384 lastIdx = focusables.length - 1;\r
4385 currIdx = Ext.Array.indexOf(focusables, currentDom);\r
4386 \r
4387 // If the currently focused element is not present in the list,\r
4388 // it must be the body itself. Just focus the first or last\r
4389 // tabbable element.\r
4390 if (currIdx < 0) {\r
4391 if (forward) {\r
4392 i = 0;\r
4393 to = len - 1;\r
4394 step = 1;\r
4395 }\r
4396 else {\r
4397 i = len - 1;\r
4398 to = 0;\r
4399 step = -1;\r
4400 }\r
4401 }\r
4402 else {\r
4403 if (forward) {\r
4404 i = currIdx + 1;\r
4405 to = len - 1;\r
4406 step = 1;\r
4407 }\r
4408 else {\r
4409 i = currIdx - 1;\r
4410 to = 0;\r
4411 step = -1;\r
4412 }\r
4413 }\r
4414 \r
4415 // We're only interested in the elements that an user can *tab into*,\r
4416 // not all programmatically focusable elements. So we have to filter\r
4417 // these out.\r
4418 for (;; i += step) {\r
4419 if ((step > 0 && i > to) || (step < 0 && i < to)) {\r
4420 break;\r
4421 }\r
4422 \r
4423 node = focusables[i];\r
4424 \r
4425 if (Ext.fly(node).isTabbable()) {\r
4426 next = node;\r
4427 break;\r
4428 }\r
4429 }\r
4430\r
4431 return Ext.get(next || body);\r
4432 }\r
4433\r
4434 from = from.isComponent ? from.getFocusEl() : from;\r
4435\r
4436 jasmine.fireKeyEvent(from, 'keydown', 9, forward);\r
4437 \r
4438 // Compute the next target *after* firing keydown;\r
4439 // a handler somewhere could have changed tabbability!\r
4440 // Not only that but the focused element could have changed\r
4441 // as well so we have to account for it.\r
4442 var to = getNextTabTarget(document.activeElement || from, forward);\r
4443 \r
4444 if (to) {\r
4445 to.focus();\r
4446 }\r
4447 \r
4448 jasmine.fireKeyEvent(to || from, 'keyup', 9, forward);\r
4449\r
4450 return to;\r
4451};\r
4452\r
4453jasmine.simulateArrowKey = jasmine.syncPressArrowKey =\r
4454jasmine.simulateKey = jasmine.syncPressKey = function(from, key, options) {\r
4455 var keyCode = Ext.event.Event[key.toUpperCase()];\r
4456 \r
4457 if (keyCode === undefined) {\r
4458 throw 'Cannot fire undefined key event!';\r
4459 }\r
4460\r
4461 from = from.isComponent ? from.getFocusEl() : from;\r
4462\r
4463 var target = Ext.getDom(from);\r
4464\r
4465 if (!target) {\r
4466 throw 'Cannot fire arrow key event on null element';\r
4467 }\r
4468 \r
4469 var shiftKey, ctrlKey, altKey;\r
4470 \r
4471 if (options) {\r
4472 shiftKey = options.shift || options.shiftKey;\r
4473 ctrlKey = options.ctrl || options.ctrlKey;\r
4474 altKey = options.alt || options.altKey;\r
4475 }\r
4476\r
4477 jasmine.fireKeyEvent(target, 'keydown', keyCode, shiftKey, ctrlKey, altKey);\r
4478 \r
4479 // IE8 blows up with "unspecified error" :(\r
4480 if (!Ext.isIE8) {\r
4481 jasmine.fireKeyEvent(target, 'keyup', keyCode, shiftKey, ctrlKey, altKey);\r
4482 }\r
4483};\r
4484\r
4485// In IE, focus events are asynchronous so we often have to wait\r
4486// after attempting to focus something. Otherwise tests will fail.\r
4487jasmine.waitForFocus = jasmine.waitsForFocus = function(cmp, desc, timeout) {\r
4488 var isComponent = cmp.isComponent,\r
4489 dom = isComponent ? cmp.getFocusEl().dom\r
4490 : cmp.isElement ? cmp.dom\r
4491 : cmp,\r
4492 id = isComponent ? (cmp.itemId || cmp.id) : dom.id;\r
4493 \r
4494 if (!desc) {\r
4495 desc = id + ' to focus';\r
4496 }\r
4497\r
4498 timeout = timeout || 1000;\r
4499\r
4500 waitsFor(\r
4501 function() {\r
4502 return document.activeElement === dom;\r
4503 },\r
4504 desc,\r
4505 timeout\r
4506 );\r
4507};\r
4508\r
4509jasmine.waitForBlur = jasmine.waitsForBlur = function(cmp, desc, timeout) {\r
4510 var dom = cmp.isComponent ? cmp.getFocusEl().dom\r
4511 : cmp.isElement ? cmp.dom\r
4512 : cmp;\r
4513 \r
4514 if (!desc) {\r
4515 desc = dom.id + ' to blur';\r
4516 }\r
4517\r
4518 timeout = timeout || 1000;\r
4519\r
4520 waitsFor(\r
4521 function() {\r
4522 return document.activeElement !== dom;\r
4523 },\r
4524 desc,\r
4525 timeout\r
4526 );\r
4527};\r
4528\r
4529// In IE (all of 'em), focus/blur events are asynchronous. To us it means\r
4530// not only that we have to wait for the actual element to focus but\r
4531// also for its container-injected focus handler to fire; and since\r
4532// container focus handler may focus yet another element we have to yield\r
4533// for *that* focus handler to fire, too. The third `waits` is to\r
4534// accommodate for any repercussions caused by secondary focus handler,\r
4535// and of course as a good luck charm.\r
4536// Note that the timeout value is not important here because effectively\r
4537// we just want to yield enough cycles to unwind all the async event handlers\r
4538// before the test checks done in the specs, so we default to 1 ms.\r
4539jasmine.waitAWhile = jasmine.waitsAWhile = function(timeout) {\r
4540 timeout = timeout != null ? timeout : 1;\r
4541\r
4542 waits(timeout);\r
4543 waits(timeout);\r
4544 waits(timeout);\r
4545};\r
4546\r
4547jasmine.focusAndWait = function(cmp, waitFor) {\r
4548 // Apparently IE has yet another odd problem with focusing some elements;\r
4549 // if dom.focus() is called before the element is fully initialized, focusing\r
4550 // will fail and focus will jump to the document body. This happens with\r
4551 // text inputs at the very least, maybe with some others as well.\r
4552 // In IE9-10 we work around this issue by giving it a bit of time to finish\r
4553 // whatever initialization it was doing; in IE8 some harsher measures are\r
4554 // required, see Ext.dom.Element override.\r
4555 if (Ext.isIE10m) {\r
4556 jasmine.waitAWhile();\r
4557 }\r
4558 \r
4559 runs(function() {\r
4560 cmp.focus();\r
4561 });\r
4562\r
4563 jasmine.waitForFocus(waitFor || cmp);\r
4564\r
4565 jasmine.waitAWhile();\r
4566};\r
4567\r
4568jasmine.blurAndWait = function(cmp, waitFor) {\r
4569 runs(function() {\r
4570 // Programmatic blur fails on IEs, so just focus the body element instead.\r
4571 if (Ext.isIE) {\r
4572 Ext.getBody().focus();\r
4573 }\r
4574 else {\r
4575 cmp.blur();\r
4576 }\r
4577 });\r
4578\r
4579 jasmine.waitForBlur(waitFor || cmp);\r
4580\r
4581 jasmine.waitAWhile();\r
4582};\r
4583\r
4584jasmine.pressTabKey = jasmine.asyncPressTabKey = function(from, forward) {\r
4585 jasmine.focusAndWait(from, from);\r
4586\r
4587 runs(function() {\r
4588 jasmine.simulateTabKey(from, forward);\r
4589 });\r
4590\r
4591 jasmine.waitAWhile();\r
4592};\r
4593\r
4594jasmine.pressArrowKey = jasmine.asyncPressArrowKey =\r
4595jasmine.pressKey = jasmine.asyncPressKey = function(from, key, options) {\r
4596 jasmine.focusAndWait(from);\r
4597\r
4598 runs(function() {\r
4599 jasmine.simulateArrowKey(from, key, options);\r
4600 });\r
4601\r
4602 jasmine.waitAWhile();\r
4603};\r
4604\r
4605// Can't add this one and below as simple matchers,\r
4606// because there's async waiting involved\r
4607jasmine.expectFocused = jasmine.expectsFocused = function(want, noWait) {\r
4608 if (!noWait) {\r
4609 jasmine.waitForFocus(want);\r
4610 }\r
4611\r
4612 runs(function() {\r
4613 var have = want.isComponent ? Ext.ComponentManager.getActiveComponent()\r
4614 : want.isElement ? Ext.fly(document.activeElement)\r
4615 : document.activeElement\r
4616 ;\r
4617 \r
4618 expect(have).toBe(want);\r
4619 });\r
4620};\r
4621\r
4622jasmine.expectTabIndex = jasmine.expectsTabIndex = function(wantIndex, el) {\r
4623 runs(function() {\r
4624 if (el && el.isComponent) {\r
4625 el = el.getFocusEl();\r
4626 }\r
4627\r
4628 var haveIndex = el.dom.getAttribute('tabIndex');\r
4629\r
4630 expect(haveIndex - 0).toBe(wantIndex);\r
4631 });\r
4632};\r
4633\r
4634jasmine.expectAriaAttr = function(cmp, attr, value) {\r
4635 var target = cmp.isComponent ? cmp.ariaEl : Ext.get(cmp);\r
4636 \r
4637 expect(target.dom.getAttribute(attr)).toBe(value);\r
4638};\r
4639\r
4640jasmine.expectNoAriaAttr = function(cmp, attr) {\r
4641 var target = cmp.isComponent ? cmp.ariaEl : Ext.get(cmp);\r
4642 \r
4643 expect(target.dom.hasAttribute(attr)).toBe(false);\r
4644};\r
4645\r
4646/*\r
4647This version is commented out because there is a bug in WebKit that prevents\r
4648key events to be fired with both options and a valid keycode. When the bug gets fixed\r
4649this method can be reintroduced. See https://bugs.webkit.org/show_bug.cgi?id=16735\r
4650jasmine.fireKeyEvent = function(target, type, key, options) {\r
4651 var e, ret, prop;\r
4652 \r
4653 options = options || {};\r
4654 target = Ext.getDom(target);\r
4655 if (document.createEventObject) { //IE event model\r
4656 e = document.createEventObject();\r
4657 Ext.apply(e, {\r
4658 bubbles: true,\r
4659 cancelable: true,\r
4660 keyCode: key\r
4661 });\r
4662 if (options) {\r
4663 for (prop in options) {\r
4664 if (options.hasOwnProperty(prop)) {\r
4665 e[prop] = options[prop];\r
4666 }\r
4667 }\r
4668 }\r
4669 return target.fireEvent('on' + type, e);\r
4670 }\r
4671 else {\r
4672 e = document.createEvent('KeyboardEvent');\r
4673 if (typeof e.initKeyboardEvent != 'undefined') {\r
4674 e.initKeyboardEvent(type, true, true, window,\r
4675 false,\r
4676 false,\r
4677 false,\r
4678 false, 97, 97);\r
4679 } else {\r
4680 e.initKeyEvent(type, true, true, window,\r
4681 options.ctrlKey || false,\r
4682 options.altKey || false,\r
4683 options.shiftKey || false,\r
4684 options.metaKey || false, key, key);\r
4685 }\r
4686 return target.dispatchEvent(e);\r
4687 }\r
4688};\r
4689*/\r
4690var fakeScope = {\r
4691 id: "fakeScope",\r
4692 fakeScope: true\r
4693};\r
4694/**\r
4695 * Class to act as a bridge between the MockAjax class and Ext.data.Request\r
4696 */\r
4697var MockAjaxManager = {\r
4698 \r
4699 getXhrInstance: null,\r
4700 \r
4701 /**\r
4702 * Pushes methods onto the Request prototype to make it easier to deal with\r
4703 */\r
4704 addMethods: function(){\r
4705 var Connection = Ext.data.Connection,\r
4706 connectionProto = Connection.prototype,\r
4707 requestProto = Ext.data.request.Ajax.prototype;\r
4708 \r
4709 Connection.requestId = 0;\r
4710 MockAjaxManager.getXhrInstance = requestProto.getXhrInstance;\r
4711 \r
4712 /**\r
4713 * Template method to create the AJAX request\r
4714 */\r
4715 requestProto.getXhrInstance = function() {\r
4716 return new MockAjax(); \r
4717 };\r
4718 \r
4719 /**\r
4720 * Method to simulate a request completing\r
4721 * @param {Object} response The response\r
4722 * @param {String} id (optional) The id of the completed request\r
4723 */\r
4724 connectionProto.mockComplete = function(response, id) {\r
4725 var request = this.mockGetRequestXHR(id);\r
4726 \r
4727 if (request) {\r
4728 request.xhr.complete(response);\r
4729 }\r
4730 };\r
4731 \r
4732 /**\r
4733 * Get a particular request\r
4734 * @param {String} id (optional) The id of the request\r
4735 */\r
4736 connectionProto.mockGetRequestXHR = function(id) {\r
4737 var request;\r
4738 \r
4739 if (id) {\r
4740 request = this.requests[id];\r
4741 } else {\r
4742 // get the first one\r
4743 request = this.mockGetAllRequests()[0];\r
4744 }\r
4745 return request ? request : null;\r
4746 };\r
4747 \r
4748 /**\r
4749 * Gets all the requests from the Connection\r
4750 */\r
4751 connectionProto.mockGetAllRequests = function(){\r
4752 var requests = this.requests,\r
4753 id,\r
4754 request,\r
4755 out = [];\r
4756 \r
4757 for (id in requests) {\r
4758 if (requests.hasOwnProperty(id)) {\r
4759 out.push(requests[id]);\r
4760 }\r
4761 }\r
4762 return out;\r
4763 };\r
4764\r
4765 this.originalExtAjax = Ext.Ajax;\r
4766 Ext.Ajax = new Connection({ autoAbort : false });\r
4767 },\r
4768 \r
4769 /**\r
4770 * Restore any changes made by addMethods\r
4771 */\r
4772 removeMethods: function() {\r
4773 var proto = Ext.data.Connection.prototype;\r
4774 delete proto.mockComplete;\r
4775 delete proto.mockGetRequestXHR;\r
4776 Ext.Ajax = this.originalExtAjax;\r
4777 \r
4778 Ext.data.request.Ajax.prototype.getXhrInstance = MockAjaxManager.getXhrInstance;\r
4779 MockAjaxManager.getXhrInstance = null;\r
4780 }\r
4781};\r
4782\r
4783/**\r
4784 * Simple Mock class to represent an XMLHttpRequest\r
4785 */\r
4786var MockAjax = function(){\r
4787 /**\r
4788 * Contains all request headers\r
4789 */\r
4790 this.headers = {};\r
4791 \r
4792 /**\r
4793 * Contains any options specified during sending\r
4794 */\r
4795 this.ajaxOptions = {};\r
4796 \r
4797 this.readyState = 0;\r
4798 \r
4799 this.status = null;\r
4800 \r
4801 this.responseText = this.responseXML = null;\r
4802};\r
4803\r
4804/**\r
4805 * Contains a default response for any synchronous request.\r
4806 */\r
4807MockAjax.prototype.syncDefaults = {\r
4808 responseText: 'data',\r
4809 status: 200,\r
4810 statusText: '',\r
4811 responseXML: null,\r
4812 responseHeaders: {"Content-type": "application/json" }\r
4813};\r
4814\r
4815MockAjax.prototype.readyChange = function() {\r
4816 if (this.onreadystatechange) {\r
4817 this.onreadystatechange();\r
4818 }\r
4819};\r
4820\r
4821/**\r
4822 * Simulate the XHR open method\r
4823 * @param {Object} method\r
4824 * @param {Object} url\r
4825 * @param {Object} async\r
4826 * @param {Object} username\r
4827 * @param {Object} password\r
4828 */\r
4829MockAjax.prototype.open = function(method, url, async, username, password){\r
4830 var options = this.ajaxOptions;\r
4831 options.method = method;\r
4832 options.url = url;\r
4833 options.async = async;\r
4834 options.username = username;\r
4835 options.password = password;\r
4836 this.readyState = 1;\r
4837 this.readyChange();\r
4838};\r
4839\r
4840/**\r
4841 * Simulate the XHR send method\r
4842 * @param {Object} data\r
4843 */\r
4844MockAjax.prototype.send = function(data){\r
4845 this.ajaxOptions.data = data;\r
4846 this.readyState = 2;\r
4847 // if it's a synchronous request, let's just assume it's already finished\r
4848 if (!this.ajaxOptions.async) {\r
4849 this.complete(this.syncDefaults);\r
4850 } else {\r
4851 this.readyChange();\r
4852 }\r
4853};\r
4854\r
4855/**\r
4856 * Simulate the XHR abort method\r
4857 */\r
4858MockAjax.prototype.abort = function(){\r
4859 this.readyState = 0;\r
4860 this.readyChange();\r
4861};\r
4862\r
4863/**\r
4864 * Simulate the XHR setRequestHeader method\r
4865 * @param {Object} header\r
4866 * @param {Object} value\r
4867 */\r
4868MockAjax.prototype.setRequestHeader = function(header, value){\r
4869 this.headers[header] = value;\r
4870};\r
4871\r
4872/**\r
4873 * Simulate the XHR getAllResponseHeaders method\r
4874 */\r
4875MockAjax.prototype.getAllResponseHeaders = function(){\r
4876 var headers = this.responseHeaders,\r
4877 lines = [],\r
4878 header;\r
4879 \r
4880 for (header in headers) {\r
4881 if (headers.hasOwnProperty(header)) {\r
4882 lines.push(header + ': ' + headers[header]);\r
4883 }\r
4884 }\r
4885 \r
4886 return lines.join('\r\n');\r
4887};\r
4888\r
4889/**\r
4890 * Simulate the XHR getResponseHeader method\r
4891 * @param {Object} name\r
4892 */\r
4893MockAjax.prototype.getResponseHeader = function(name){\r
4894 return this.responseHeaders[header];\r
4895};\r
4896\r
4897/**\r
4898 * Simulate the XHR onreadystatechange method\r
4899 */\r
4900MockAjax.prototype.onreadystatechange = function(){\r
4901};\r
4902\r
4903/**\r
4904 * Method for triggering a response completion\r
4905 */\r
4906MockAjax.prototype.complete = function(response){\r
4907 this.responseText = response.responseText || '';\r
4908 this.status = response.status;\r
4909 this.statusText = response.statusText;\r
4910 this.responseXML = response.responseXML || this.xmlDOM(response.responseText);\r
4911 this.responseHeaders = response.responseHeaders || {"Content-type": response.contentType || "application/json" };\r
4912 this.readyState = 4;\r
4913 this.readyChange();\r
4914};\r
4915\r
4916/**\r
4917 * Converts string to XML DOM\r
4918 */\r
4919MockAjax.prototype.xmlDOM = function(xml) {\r
4920 // IE DOMParser support\r
4921 if (!window.DOMParser && window.ActiveXObject) {\r
4922 doc = new ActiveXObject('Microsoft.XMLDOM');\r
4923 doc.async = 'false';\r
4924 DOMParser = function() {};\r
4925 DOMParser.prototype.parseFromString = function(xmlString) {\r
4926 var doc = new ActiveXObject('Microsoft.XMLDOM');\r
4927 doc.async = 'false';\r
4928 doc.loadXML(xmlString);\r
4929 return doc;\r
4930 };\r
4931 } \r
4932\r
4933 if (xml && xml.substr(0, 1) === '<') {\r
4934 try {\r
4935 return (new DOMParser()).parseFromString(xml, "text/xml");\r
4936 }\r
4937 catch (e) {}\r
4938 }\r
4939 \r
4940 return null;\r
4941};\r