]> git.proxmox.com Git - extjs.git/blame - extjs/classic/locale/.sencha/test/jasmine.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / locale / .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 */\r
558var waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {\r
559 jasmine.getEnv().currentSpec.waitsFor.apply(jasmine.getEnv().currentSpec, arguments);\r
560};\r
561if (isCommonJS) exports.waitsFor = waitsFor;\r
562\r
563/**\r
564 * A function that is called before each spec in a suite.\r
565 *\r
566 * Used for spec setup, including validating assumptions.\r
567 *\r
568 * @param {Function} beforeEachFunction\r
569 */\r
570var beforeEach = function(beforeEachFunction) {\r
571 jasmine.getEnv().beforeEach(beforeEachFunction);\r
572};\r
573if (isCommonJS) exports.beforeEach = beforeEach;\r
574\r
575/**\r
576 * A function that is called after each spec in a suite.\r
577 *\r
578 * Used for restoring any state that is hijacked during spec execution.\r
579 *\r
580 * @param {Function} afterEachFunction\r
581 */\r
582var afterEach = function(afterEachFunction) {\r
583 jasmine.getEnv().afterEach(afterEachFunction);\r
584};\r
585if (isCommonJS) exports.afterEach = afterEach;\r
586\r
587/**\r
588 * Defines a suite of specifications.\r
589 *\r
590 * Stores the description and all defined specs in the Jasmine environment as one suite of specs. Variables declared\r
591 * are accessible by calls to beforeEach, it, and afterEach. Describe blocks can be nested, allowing for specialization\r
592 * of setup in some tests.\r
593 *\r
594 * @example\r
595 * // TODO: a simple suite\r
596 *\r
597 * // TODO: a simple suite with a nested describe block\r
598 *\r
599 * @param {String} description A string, usually the class under test.\r
600 * @param {Function} specDefinitions function that defines several specs.\r
601 */\r
602var describe = function(description, specDefinitions) {\r
603 return jasmine.getEnv().describe(description, specDefinitions);\r
604};\r
605if (isCommonJS) exports.describe = describe;\r
606\r
607/**\r
608 * Disables a suite of specifications. Used to disable some suites in a file, or files, temporarily during development.\r
609 *\r
610 * @param {String} description A string, usually the class under test.\r
611 * @param {Function} specDefinitions function that defines several specs.\r
612 */\r
613var xdescribe = function(description, specDefinitions) {\r
614 return jasmine.getEnv().xdescribe(description, specDefinitions);\r
615};\r
616if (isCommonJS) exports.xdescribe = xdescribe;\r
617\r
618\r
619// Provide the XMLHttpRequest class for IE 5.x-6.x:\r
620jasmine.XmlHttpRequest = (typeof XMLHttpRequest == "undefined") ? function() {\r
621 function tryIt(f) {\r
622 try {\r
623 return f();\r
624 } catch(e) {\r
625 }\r
626 return null;\r
627 }\r
628\r
629 var xhr = tryIt(function() {\r
630 return new ActiveXObject("Msxml2.XMLHTTP.6.0");\r
631 }) ||\r
632 tryIt(function() {\r
633 return new ActiveXObject("Msxml2.XMLHTTP.3.0");\r
634 }) ||\r
635 tryIt(function() {\r
636 return new ActiveXObject("Msxml2.XMLHTTP");\r
637 }) ||\r
638 tryIt(function() {\r
639 return new ActiveXObject("Microsoft.XMLHTTP");\r
640 });\r
641\r
642 if (!xhr) throw new Error("This browser does not support XMLHttpRequest.");\r
643\r
644 return xhr;\r
645} : XMLHttpRequest;\r
646jasmine.MAX_PRETTY_PRINT_DEPTH = 1;\r
647\r
648jasmine.hashes = {};\r
649\r
650jasmine.hashString = function (s, hash) {\r
651 hash = hash || 0;\r
652 \r
653 // see http://www.cse.yorku.ca/~oz/hash.html\r
654 for (var c, i = 0, n = s.length; i < n; ++i) {\r
655 c = s.charCodeAt(i);\r
656 hash = c + (hash << 6) + (hash << 16) - hash;\r
657 }\r
658 \r
659 if (jasmine.hashes[hash]) {\r
660 console.log("Identical hash detected: " + s);\r
661 }\r
662 \r
663 jasmine.hashes[hash] = true;\r
664 return hash;\r
665};\r
666\r
667jasmine.toMap = function (items) {\r
668 var map = {},\r
669 k;\r
670\r
671 for (k = items.length; k--; ) {\r
672 map[items[k]] = true;\r
673 }\r
674\r
675 return map;\r
676};\r
677\r
678\r
679jasmine.setCurrentScript = function(file){\r
680 if(typeof Ext !== "undefined" && Ext.cmd && Ext.cmd.api && Ext.cmd.api.adapter) {\r
681 Ext.cmd.api.adapter.setCurrentScript(file);\r
682 }\r
683};\r
684\r
685jasmine.getCurrentScript = function() {\r
686 if(typeof Ext !== "undefined" && Ext.cmd && Ext.cmd.api && Ext.cmd.api.adapter) {\r
687 return Ext.cmd.api.adapter.getCurrentScript();\r
688 }\r
689 return null;\r
690};\r
691\r
692jasmine.setOptions = function(jsonString) {\r
693 jasmine._options = JSON.parse(jsonString);\r
694};\r
695\r
696jasmine.getOptions = function() {\r
697 return jasmine._options || {};\r
698};\r
699\r
700jasmine.initDebug = function() {\r
701 var spec = jasmine.getOptions().spec;\r
702 if (spec) {\r
703 var specId = parseInt(spec);\r
704 this.getEnv().specFilter = function(spec) {\r
705 if (spec.id === specId) {\r
706 spec.debugBlocks = true;\r
707 return true;\r
708 } else {\r
709 return false;\r
710 }\r
711 }\r
712 }\r
713};\r
714\r
715jasmine.generateDebuggableBlock = function(fn) {\r
716 return function() {\r
717 debugger;\r
718 /* Step into the function below */\r
719 fn.apply(this, arguments);\r
720 };\r
721};\r
722\r
723jasmine.showDebugPrompt = function(callback) {\r
724 if (navigator.userAgent.toLowerCase().match(/chrome|safari|msie/)) {\r
725 var div = document.createElement("div");\r
726 \r
727 div.setAttribute("style",[ \r
728 "background:#E5E4E2;",\r
729 "z-index:99999;",\r
730 "position:absolute;",\r
731 "left:25%;",\r
732 "right:25%;",\r
733 "top:100px;",\r
734 "border-radius: 5px;",\r
735 "border:1px solid #777;",\r
736 "text-align:center;",\r
737 "box-shadow: 5px 5px 5px #888;"\r
738 ].join(""));\r
739 \r
740 div.innerHTML = [\r
741 '<p>Open the developer tools to debug and press ok.</p>',\r
742 '<button id="sencha-debug-button">OK</button>',\r
743 '<p></p>'\r
744 ].join("");\r
745 \r
746 document.body.appendChild(div);\r
747 \r
748 var button = document.getElementById("sencha-debug-button");\r
749 \r
750 var onClick = function() {\r
751 if (button.removeEventListener) {\r
752 button.removeEventListener("click", onClick, false);\r
753 } else {\r
754 button.detachEvent("onmousedown", onClick);\r
755 }\r
756 document.body.removeChild(div);\r
757 div = null;\r
758 callback();\r
759 };\r
760 \r
761 if (button.addEventListener) {\r
762 button.addEventListener("click", onClick, false);\r
763 } else {\r
764 button.attachEvent("onmousedown", onClick);\r
765 }\r
766 } else {\r
767 callback();\r
768 }\r
769};\r
770\r
771\r
772addGlobal = function() {};\r
773\r
774jasmine.getByIds = function (items, ids) {\r
775 var result = [],\r
776 length = items.length,\r
777 i, id, item;\r
778\r
779 for (i = 0; i < length; ++i) {\r
780 item = items[i];\r
781 id = item.id;\r
782 if (ids[id]) {\r
783 result.push(item);\r
784 }\r
785 }\r
786\r
787 return result;\r
788};\r
789\r
790var specFor = function(object, specForFn) {\r
791 jasmine.getEnv().specFor(object, specForFn);\r
792};\r
793\r
794var xspecFor = function(object, specForFn) {\r
795 jasmine.getEnv().xspecFor(object, specForFn);\r
796};\r
797\r
798var xdescribe = function(description, specDefinitions, coverageFile) {\r
799 return jasmine.getEnv().describe(description, specDefinitions, coverageFile).disable();\r
800};\r
801\r
802var xit = function(desc, func) {\r
803 return jasmine.getEnv().it(desc, func).disable();\r
804};\r
805/**\r
806 * @namespace\r
807 */\r
808jasmine.util = {};\r
809\r
810/**\r
811 * Declare that a child class inherit it's prototype from the parent class.\r
812 *\r
813 * @private\r
814 * @param {Function} childClass\r
815 * @param {Function} parentClass\r
816 */\r
817jasmine.util.inherit = function(childClass, parentClass) {\r
818 /**\r
819 * @private\r
820 */\r
821 var subclass = function() {\r
822 };\r
823 subclass.prototype = parentClass.prototype;\r
824 childClass.prototype = new subclass();\r
825};\r
826\r
827jasmine.util.formatException = function(e) {\r
828 var lineNumber;\r
829 if (e.line) {\r
830 lineNumber = e.line;\r
831 }\r
832 else if (e.lineNumber) {\r
833 lineNumber = e.lineNumber;\r
834 }\r
835\r
836 var file;\r
837\r
838 if (e.sourceURL) {\r
839 file = e.sourceURL;\r
840 }\r
841 else if (e.fileName) {\r
842 file = e.fileName;\r
843 }\r
844\r
845 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\r
846\r
847 if (file && lineNumber) {\r
848 message += ' in ' + file + ' (line ' + lineNumber + ')';\r
849 }\r
850\r
851 return message;\r
852};\r
853\r
854jasmine.util.htmlEscape = function(str) {\r
855 if (!str) return str;\r
856 return str.replace(/&/g, '&amp;')\r
857 .replace(/</g, '&lt;')\r
858 .replace(/>/g, '&gt;');\r
859};\r
860\r
861jasmine.util.argsToArray = function(args) {\r
862 var arrayOfArgs = [];\r
863 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);\r
864 return arrayOfArgs;\r
865};\r
866\r
867jasmine.util.extend = function(destination, source) {\r
868 for (var property in source) destination[property] = source[property];\r
869 return destination;\r
870};\r
871\r
872jasmine.util.getOrigin = function() {\r
873 var port = window.location.port;\r
874 var origin;\r
875\r
876 origin = window.location.protocol + "//" + window.location.hostname;\r
877\r
878 if (port) {\r
879 origin += ":" + port;\r
880 }\r
881\r
882 return origin;\r
883};\r
884\r
885jasmine.util.getFileFromContextMapping = function(file) {\r
886 var contextMapping = jasmine.contextMapping;\r
887 if (file && contextMapping) {\r
888 var origin = jasmine.util.getOrigin();\r
889 for (var context in contextMapping) {\r
890 file = file.replace(origin + context, contextMapping[context]);\r
891 }\r
892 }\r
893 return file;\r
894};\r
895\r
896jasmine.util.formatException = function(e) {\r
897 var lineNumber;\r
898 if (e.line) {\r
899 lineNumber = e.line;\r
900 }\r
901 else if (e.lineNumber) {\r
902 lineNumber = e.lineNumber;\r
903 }\r
904\r
905 var file;\r
906\r
907 if (e.sourceURL) {\r
908 file = e.sourceURL;\r
909 }\r
910 else if (e.fileName) {\r
911 file = e.fileName;\r
912 }\r
913\r
914 file = jasmine.util.getFileFromContextMapping(file);\r
915 \r
916 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\r
917\r
918 if (file && lineNumber) {\r
919 message += ' in ' + file + ' (line ' + lineNumber + ')';\r
920 }\r
921\r
922 return message;\r
923};/**\r
924 * Environment for Jasmine\r
925 *\r
926 * @constructor\r
927 */\r
928jasmine.Env = function() {\r
929 this.currentSpec = null;\r
930 this.currentSuite = null;\r
931 this.currentRunner_ = new jasmine.Runner(this);\r
932\r
933 this.reporter = new jasmine.MultiReporter();\r
934\r
935 this.updateInterval = jasmine.DEFAULT_UPDATE_INTERVAL;\r
936 this.defaultTimeoutInterval = jasmine.DEFAULT_TIMEOUT_INTERVAL;\r
937 this.lastUpdate = 0;\r
938 this.specFilter = function() {\r
939 return true;\r
940 };\r
941\r
942 this.nextSpecId_ = 0;\r
943 this.nextSuiteId_ = 0;\r
944 this.equalityTesters_ = [];\r
945\r
946 // wrap matchers\r
947 this.matchersClass = function() {\r
948 jasmine.Matchers.apply(this, arguments);\r
949 };\r
950 jasmine.util.inherit(this.matchersClass, jasmine.Matchers);\r
951\r
952 jasmine.Matchers.wrapInto_(jasmine.Matchers.prototype, this.matchersClass);\r
953};\r
954\r
955\r
956jasmine.Env.prototype.setTimeout = jasmine.setTimeout;\r
957jasmine.Env.prototype.clearTimeout = jasmine.clearTimeout;\r
958jasmine.Env.prototype.setInterval = jasmine.setInterval;\r
959jasmine.Env.prototype.clearInterval = jasmine.clearInterval;\r
960\r
961/**\r
962 * @returns an object containing jasmine version build info, if set.\r
963 */\r
964jasmine.Env.prototype.version = function () {\r
965 if (jasmine.version_) {\r
966 return jasmine.version_;\r
967 } else {\r
968 throw new Error('Version not set');\r
969 }\r
970};\r
971\r
972/**\r
973 * @returns string containing jasmine version build info, if set.\r
974 */\r
975jasmine.Env.prototype.versionString = function() {\r
976 if (!jasmine.version_) {\r
977 return "version unknown";\r
978 }\r
979\r
980 var version = this.version();\r
981 var versionString = version.major + "." + version.minor + "." + version.build;\r
982 if (version.release_candidate) {\r
983 versionString += ".rc" + version.release_candidate;\r
984 }\r
985 versionString += " revision " + version.revision;\r
986 return versionString;\r
987};\r
988\r
989/**\r
990 * @returns a sequential integer starting at 0\r
991 */\r
992jasmine.Env.prototype.nextSpecId = function () {\r
993 return this.nextSpecId_++;\r
994};\r
995\r
996/**\r
997 * @returns a sequential integer starting at 0\r
998 */\r
999jasmine.Env.prototype.nextSuiteId = function () {\r
1000 return this.nextSuiteId_++;\r
1001};\r
1002\r
1003/**\r
1004 * Register a reporter to receive status updates from Jasmine.\r
1005 * @param {jasmine.Reporter} reporter An object which will receive status updates.\r
1006 */\r
1007jasmine.Env.prototype.addReporter = function(reporter) {\r
1008 this.reporter.addReporter(reporter);\r
1009};\r
1010\r
1011jasmine.Env.prototype.execute = function() {\r
1012 this.currentRunner_.execute();\r
1013};\r
1014\r
1015jasmine.Env.prototype.describe = function(description, specDefinitions) {\r
1016 var suite = new jasmine.Suite(this, description, specDefinitions, this.currentSuite);\r
1017\r
1018 var parentSuite = this.currentSuite;\r
1019 if (parentSuite) {\r
1020 parentSuite.add(suite);\r
1021 } else {\r
1022 this.currentRunner_.add(suite);\r
1023 }\r
1024\r
1025 this.currentSuite = suite;\r
1026\r
1027 var declarationError = null;\r
1028 try {\r
1029 specDefinitions.call(suite);\r
1030 } catch(e) {\r
1031 declarationError = e;\r
1032 }\r
1033\r
1034 if (declarationError) {\r
1035 this.it("encountered a declaration exception", function() {\r
1036 throw declarationError;\r
1037 });\r
1038 }\r
1039\r
1040 this.currentSuite = parentSuite;\r
1041\r
1042 return suite;\r
1043};\r
1044\r
1045jasmine.Env.prototype.beforeEach = function(beforeEachFunction) {\r
1046 if (this.currentSuite) {\r
1047 this.currentSuite.beforeEach(beforeEachFunction);\r
1048 } else {\r
1049 this.currentRunner_.beforeEach(beforeEachFunction);\r
1050 }\r
1051};\r
1052\r
1053jasmine.Env.prototype.currentRunner = function () {\r
1054 return this.currentRunner_;\r
1055};\r
1056\r
1057jasmine.Env.prototype.afterEach = function(afterEachFunction) {\r
1058 if (this.currentSuite) {\r
1059 this.currentSuite.afterEach(afterEachFunction);\r
1060 } else {\r
1061 this.currentRunner_.afterEach(afterEachFunction);\r
1062 }\r
1063\r
1064};\r
1065\r
1066jasmine.Env.prototype.xdescribe = function(desc, specDefinitions) {\r
1067 return {\r
1068 execute: function() {\r
1069 }\r
1070 };\r
1071};\r
1072\r
1073jasmine.Env.prototype.it = function(description, func) {\r
1074 var spec = new jasmine.Spec(this, this.currentSuite, description);\r
1075 this.currentSuite.add(spec);\r
1076 this.currentSpec = spec;\r
1077\r
1078 if (func) {\r
1079 spec.runs(func);\r
1080 }\r
1081\r
1082 return spec;\r
1083};\r
1084\r
1085jasmine.Env.prototype.xit = function(desc, func) {\r
1086 return {\r
1087 id: this.nextSpecId(),\r
1088 runs: function() {\r
1089 }\r
1090 };\r
1091};\r
1092\r
1093jasmine.Env.prototype.compareRegExps_ = function(a, b, mismatchKeys, mismatchValues) {\r
1094 if (a.source != b.source)\r
1095 mismatchValues.push("expected pattern /" + b.source + "/ is not equal to the pattern /" + a.source + "/");\r
1096\r
1097 if (a.ignoreCase != b.ignoreCase)\r
1098 mismatchValues.push("expected modifier i was" + (b.ignoreCase ? " " : " not ") + "set and does not equal the origin modifier");\r
1099\r
1100 if (a.global != b.global)\r
1101 mismatchValues.push("expected modifier g was" + (b.global ? " " : " not ") + "set and does not equal the origin modifier");\r
1102\r
1103 if (a.multiline != b.multiline)\r
1104 mismatchValues.push("expected modifier m was" + (b.multiline ? " " : " not ") + "set and does not equal the origin modifier");\r
1105\r
1106 if (a.sticky != b.sticky)\r
1107 mismatchValues.push("expected modifier y was" + (b.sticky ? " " : " not ") + "set and does not equal the origin modifier");\r
1108\r
1109 return (mismatchValues.length === 0);\r
1110};\r
1111\r
1112jasmine.Env.prototype.compareObjects_ = function(a, b, mismatchKeys, mismatchValues) {\r
1113 if (a.__Jasmine_been_here_before__ === b && b.__Jasmine_been_here_before__ === a) {\r
1114 return true;\r
1115 }\r
1116\r
1117 a.__Jasmine_been_here_before__ = b;\r
1118 b.__Jasmine_been_here_before__ = a;\r
1119\r
1120 var hasKey = function(obj, keyName) {\r
1121 return obj !== null && obj[keyName] !== jasmine.undefined;\r
1122 };\r
1123\r
1124 for (var property in b) {\r
1125 if (!hasKey(a, property) && hasKey(b, property)) {\r
1126 mismatchKeys.push("expected has key '" + property + "', but missing from actual.");\r
1127 }\r
1128 }\r
1129 for (property in a) {\r
1130 if (!hasKey(b, property) && hasKey(a, property)) {\r
1131 mismatchKeys.push("expected missing key '" + property + "', but present in actual.");\r
1132 }\r
1133 }\r
1134 for (property in b) {\r
1135 if (property == '__Jasmine_been_here_before__') continue;\r
1136 if (!this.equals_(a[property], b[property], mismatchKeys, mismatchValues)) {\r
1137 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
1138 }\r
1139 }\r
1140\r
1141 if (jasmine.isArray_(a) && jasmine.isArray_(b) && a.length != b.length) {\r
1142 mismatchValues.push("arrays were not the same length");\r
1143 }\r
1144\r
1145 delete a.__Jasmine_been_here_before__;\r
1146 delete b.__Jasmine_been_here_before__;\r
1147 return (mismatchKeys.length === 0 && mismatchValues.length === 0);\r
1148};\r
1149\r
1150jasmine.Env.prototype.equals_ = function(a, b, mismatchKeys, mismatchValues) {\r
1151 mismatchKeys = mismatchKeys || [];\r
1152 mismatchValues = mismatchValues || [];\r
1153\r
1154 for (var i = 0; i < this.equalityTesters_.length; i++) {\r
1155 var equalityTester = this.equalityTesters_[i];\r
1156 var result = equalityTester(a, b, this, mismatchKeys, mismatchValues);\r
1157 if (result !== jasmine.undefined) return result;\r
1158 }\r
1159\r
1160 if (a === b) return true;\r
1161\r
1162 if (a === jasmine.undefined || a === null || b === jasmine.undefined || b === null) {\r
1163 return (a == jasmine.undefined && b == jasmine.undefined);\r
1164 }\r
1165\r
1166 if (jasmine.isDomNode(a) && jasmine.isDomNode(b)) {\r
1167 return a === b;\r
1168 }\r
1169\r
1170 if (a instanceof Date && b instanceof Date) {\r
1171 return a.getTime() == b.getTime();\r
1172 }\r
1173\r
1174 if (a.jasmineMatches) {\r
1175 return a.jasmineMatches(b);\r
1176 }\r
1177\r
1178 if (b.jasmineMatches) {\r
1179 return b.jasmineMatches(a);\r
1180 }\r
1181\r
1182 if (a instanceof jasmine.Matchers.ObjectContaining) {\r
1183 return a.matches(b);\r
1184 }\r
1185\r
1186 if (b instanceof jasmine.Matchers.ObjectContaining) {\r
1187 return b.matches(a);\r
1188 }\r
1189\r
1190 if (jasmine.isString_(a) && jasmine.isString_(b)) {\r
1191 return (a == b);\r
1192 }\r
1193\r
1194 if (jasmine.isNumber_(a) && jasmine.isNumber_(b)) {\r
1195 return (a == b);\r
1196 }\r
1197\r
1198 if (a instanceof RegExp && b instanceof RegExp) {\r
1199 return this.compareRegExps_(a, b, mismatchKeys, mismatchValues);\r
1200 }\r
1201\r
1202 if (typeof a === "object" && typeof b === "object") {\r
1203 return this.compareObjects_(a, b, mismatchKeys, mismatchValues);\r
1204 }\r
1205\r
1206 //Straight check\r
1207 return (a === b);\r
1208};\r
1209\r
1210jasmine.Env.prototype.contains_ = function(haystack, needle) {\r
1211 if (jasmine.isArray_(haystack)) {\r
1212 for (var i = 0; i < haystack.length; i++) {\r
1213 if (this.equals_(haystack[i], needle)) return true;\r
1214 }\r
1215 return false;\r
1216 }\r
1217 return haystack.indexOf(needle) >= 0;\r
1218};\r
1219\r
1220jasmine.Env.prototype.addEqualityTester = function(equalityTester) {\r
1221 this.equalityTesters_.push(equalityTester);\r
1222};\r
1223/**\r
1224 * Basic browsers detection.\r
1225 */\r
1226jasmine.browser = {};\r
1227jasmine.browser.isIE = !!window.ActiveXObject;\r
1228jasmine.browser.isIE6 = jasmine.browser.isIE && !window.XMLHttpRequest;\r
1229jasmine.browser.isIE7 = jasmine.browser.isIE && !!window.XMLHttpRequest && !document.documentMode;\r
1230jasmine.browser.isIE8 = jasmine.browser.isIE && !!window.XMLHttpRequest && !!document.documentMode && !window.performance;\r
1231jasmine.browser.isIE9 = jasmine.browser.isIE && !!window.performance;\r
1232jasmine.browser.isSafari3 = /safari/.test(navigator.userAgent.toLowerCase()) && /version\/3/.test(navigator.userAgent.toLowerCase());\r
1233jasmine.browser.isOpera = !!window.opera;\r
1234jasmine.browser.isOpera11 = jasmine.browser.isOpera && parseInt(window.opera.version(), 10) > 10;\r
1235\r
1236jasmine.array = {};\r
1237\r
1238 /**\r
1239 * Checks whether or not the specified item exists in the array.\r
1240 * Array.prototype.indexOf is missing in Internet Explorer, unfortunately.\r
1241 * We always have to use this static method instead for consistency\r
1242 * @param {Array} array The array to check\r
1243 * @param {Mixed} item The item to look for\r
1244 * @param {Number} from (Optional) The index at which to begin the search\r
1245 * @return {Number} The index of item in the array (or -1 if it is not found)\r
1246 */\r
1247jasmine.array.indexOf = function(array, item, from){\r
1248 if (array.indexOf) {\r
1249 return array.indexOf(item, from);\r
1250 }\r
1251 \r
1252 var i, length = array.length;\r
1253\r
1254 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){\r
1255 if (array[i] === item) {\r
1256 return i;\r
1257 }\r
1258 }\r
1259\r
1260 return -1;\r
1261};\r
1262\r
1263 /**\r
1264 * Removes the specified item from the array. If the item is not found nothing happens.\r
1265 * @param {Array} array The array\r
1266 * @param {Mixed} item The item to remove\r
1267 * @return {Array} The passed array itself\r
1268 */\r
1269jasmine.array.remove = function(array, item) {\r
1270 var index = this.indexOf(array, item);\r
1271\r
1272 if (index !== -1) {\r
1273 array.splice(index, 1);\r
1274 }\r
1275 \r
1276 return array;\r
1277};\r
1278\r
1279jasmine.Env.prototype.it = function(description, func, timeout) {\r
1280 var spec = new jasmine.Spec(this, this.currentSuite, description);\r
1281 this.currentSuite.add(spec);\r
1282 this.currentSpec = spec;\r
1283\r
1284 // override\r
1285 if (func) {\r
1286 func.typeName = 'it';\r
1287 var block = new jasmine.Block(spec.env, func, spec);\r
1288 block.timeout = parseInt(timeout);\r
1289 spec.addToQueue(block);\r
1290 }\r
1291 // end of override\r
1292 \r
1293 return spec;\r
1294};\r
1295\r
1296jasmine.Env.prototype.specFor = function(object, specForFn) {\r
1297 var index = 0,\r
1298 property;\r
1299\r
1300 for (property in object) {\r
1301 if (!object.hasOwnProperty(property)) {\r
1302 continue;\r
1303 }\r
1304 specForFn.call(this, property, object[property], index, object);\r
1305 index = index + 1;\r
1306 }\r
1307};\r
1308\r
1309jasmine.Env.prototype.xspecFor = function(object, specForFn) {};/** No-op base class for Jasmine reporters.\r
1310 *\r
1311 * @constructor\r
1312 */\r
1313jasmine.Reporter = function() {\r
1314};\r
1315\r
1316//noinspection JSUnusedLocalSymbols\r
1317jasmine.Reporter.prototype.reportRunnerStarting = function(runner) {\r
1318};\r
1319\r
1320//noinspection JSUnusedLocalSymbols\r
1321jasmine.Reporter.prototype.reportRunnerResults = function(runner) {\r
1322};\r
1323\r
1324//noinspection JSUnusedLocalSymbols\r
1325jasmine.Reporter.prototype.reportSuiteResults = function(suite) {\r
1326};\r
1327\r
1328//noinspection JSUnusedLocalSymbols\r
1329jasmine.Reporter.prototype.reportSpecStarting = function(spec) {\r
1330};\r
1331\r
1332//noinspection JSUnusedLocalSymbols\r
1333jasmine.Reporter.prototype.reportSpecResults = function(spec) {\r
1334};\r
1335\r
1336//noinspection JSUnusedLocalSymbols\r
1337jasmine.Reporter.prototype.log = function(str) {\r
1338};\r
1339\r
1340/**\r
1341 * Blocks are functions with executable code that make up a spec.\r
1342 *\r
1343 * @constructor\r
1344 * @param {jasmine.Env} env\r
1345 * @param {Function} func\r
1346 * @param {jasmine.Spec} spec\r
1347 */\r
1348jasmine.Block = function(env, func, spec) {\r
1349 this.env = env;\r
1350 this.func = func;\r
1351 this.spec = spec;\r
1352};\r
1353\r
1354jasmine.Block.prototype.execute = function(onComplete) {\r
1355 if (!jasmine.CATCH_EXCEPTIONS) {\r
1356 this.func.apply(this.spec);\r
1357 }\r
1358 else {\r
1359 try {\r
1360 this.func.apply(this.spec);\r
1361 } catch (e) {\r
1362 this.spec.fail(e);\r
1363 }\r
1364 }\r
1365 onComplete();\r
1366};\r
1367jasmine.Block.prototype.execute = function(onComplete) {\r
1368 if (this.func.length === 1) {\r
1369 \r
1370 var timeOutId = setTimeout(function(){\r
1371 onComplete();\r
1372 }, this.timeout || jasmine.DEFAULT_TIMEOUT_INTERVAL);\r
1373 \r
1374 if (!jasmine.CATCH_EXCEPTIONS) {\r
1375 this.func.call(this.spec, function() {\r
1376 clearTimeout(timeOutId);\r
1377 onComplete();\r
1378 });\r
1379 } else {\r
1380 try {\r
1381 this.func.call(this.spec, function() {\r
1382 clearTimeout(timeOutId);\r
1383 onComplete();\r
1384 });\r
1385 } catch (e) {\r
1386 this.spec.fail(e);\r
1387 onComplete();\r
1388 }\r
1389 } \r
1390 } else {\r
1391 if (!jasmine.CATCH_EXCEPTIONS) {\r
1392 this.func.apply(this.spec);\r
1393 } else {\r
1394 try {\r
1395 this.func.apply(this.spec);\r
1396 } catch (e) {\r
1397 this.spec.fail(e);\r
1398 }\r
1399 }\r
1400 onComplete();\r
1401 }\r
1402};/** JavaScript API reporter.\r
1403 *\r
1404 * @constructor\r
1405 */\r
1406jasmine.JsApiReporter = function() {\r
1407 this.started = false;\r
1408 this.finished = false;\r
1409 this.suites_ = [];\r
1410 this.results_ = {};\r
1411};\r
1412\r
1413jasmine.JsApiReporter.prototype.reportRunnerStarting = function(runner) {\r
1414 this.started = true;\r
1415 var suites = runner.topLevelSuites();\r
1416 for (var i = 0; i < suites.length; i++) {\r
1417 var suite = suites[i];\r
1418 this.suites_.push(this.summarize_(suite));\r
1419 }\r
1420};\r
1421\r
1422jasmine.JsApiReporter.prototype.suites = function() {\r
1423 return this.suites_;\r
1424};\r
1425\r
1426jasmine.JsApiReporter.prototype.summarize_ = function(suiteOrSpec) {\r
1427 var isSuite = suiteOrSpec instanceof jasmine.Suite;\r
1428 var summary = {\r
1429 id: suiteOrSpec.id,\r
1430 name: suiteOrSpec.description,\r
1431 type: isSuite ? 'suite' : 'spec',\r
1432 children: []\r
1433 };\r
1434 \r
1435 if (isSuite) {\r
1436 var children = suiteOrSpec.children();\r
1437 for (var i = 0; i < children.length; i++) {\r
1438 summary.children.push(this.summarize_(children[i]));\r
1439 }\r
1440 }\r
1441 return summary;\r
1442};\r
1443\r
1444jasmine.JsApiReporter.prototype.results = function() {\r
1445 return this.results_;\r
1446};\r
1447\r
1448jasmine.JsApiReporter.prototype.resultsForSpec = function(specId) {\r
1449 return this.results_[specId];\r
1450};\r
1451\r
1452//noinspection JSUnusedLocalSymbols\r
1453jasmine.JsApiReporter.prototype.reportRunnerResults = function(runner) {\r
1454 this.finished = true;\r
1455};\r
1456\r
1457//noinspection JSUnusedLocalSymbols\r
1458jasmine.JsApiReporter.prototype.reportSuiteResults = function(suite) {\r
1459};\r
1460\r
1461//noinspection JSUnusedLocalSymbols\r
1462jasmine.JsApiReporter.prototype.reportSpecResults = function(spec) {\r
1463 this.results_[spec.id] = {\r
1464 messages: spec.results().getItems(),\r
1465 result: spec.results().failedCount > 0 ? "failed" : "passed"\r
1466 };\r
1467};\r
1468\r
1469//noinspection JSUnusedLocalSymbols\r
1470jasmine.JsApiReporter.prototype.log = function(str) {\r
1471};\r
1472\r
1473jasmine.JsApiReporter.prototype.resultsForSpecs = function(specIds){\r
1474 var results = {};\r
1475 for (var i = 0; i < specIds.length; i++) {\r
1476 var specId = specIds[i];\r
1477 results[specId] = this.summarizeResult_(this.results_[specId]);\r
1478 }\r
1479 return results;\r
1480};\r
1481\r
1482jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){\r
1483 var summaryMessages = [];\r
1484 var messagesLength = result.messages.length;\r
1485 for (var messageIndex = 0; messageIndex < messagesLength; messageIndex++) {\r
1486 var resultMessage = result.messages[messageIndex];\r
1487 summaryMessages.push({\r
1488 text: resultMessage.type == 'log' ? resultMessage.toString() : jasmine.undefined,\r
1489 passed: resultMessage.passed ? resultMessage.passed() : true,\r
1490 type: resultMessage.type,\r
1491 message: resultMessage.message,\r
1492 trace: {\r
1493 stack: resultMessage.passed && !resultMessage.passed() ? resultMessage.trace.stack : jasmine.undefined\r
1494 }\r
1495 });\r
1496 }\r
1497\r
1498 return {\r
1499 result : result.result,\r
1500 messages : summaryMessages\r
1501 };\r
1502};\r
1503\r
1504/**\r
1505 * @constructor\r
1506 * @param {jasmine.Env} env\r
1507 * @param actual\r
1508 * @param {jasmine.Spec} spec\r
1509 */\r
1510jasmine.Matchers = function(env, actual, spec, opt_isNot) {\r
1511 this.env = env;\r
1512 this.actual = actual;\r
1513 this.spec = spec;\r
1514 this.isNot = opt_isNot || false;\r
1515 this.reportWasCalled_ = false;\r
1516};\r
1517\r
1518// todo: @deprecated as of Jasmine 0.11, remove soon [xw]\r
1519jasmine.Matchers.pp = function(str) {\r
1520 throw new Error("jasmine.Matchers.pp() is no longer supported, please use jasmine.pp() instead!");\r
1521};\r
1522\r
1523// todo: @deprecated Deprecated as of Jasmine 0.10. Rewrite your custom matchers to return true or false. [xw]\r
1524jasmine.Matchers.prototype.report = function(result, failing_message, details) {\r
1525 throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs");\r
1526};\r
1527\r
1528jasmine.Matchers.wrapInto_ = function(prototype, matchersClass) {\r
1529 for (var methodName in prototype) {\r
1530 if (methodName == 'report') continue;\r
1531 var orig = prototype[methodName];\r
1532 matchersClass.prototype[methodName] = jasmine.Matchers.matcherFn_(methodName, orig);\r
1533 }\r
1534};\r
1535\r
1536jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) {\r
1537 return function() {\r
1538 var matcherArgs = jasmine.util.argsToArray(arguments);\r
1539 var result = matcherFunction.apply(this, arguments);\r
1540\r
1541 if (this.isNot) {\r
1542 result = !result;\r
1543 }\r
1544\r
1545 if (this.reportWasCalled_) return result;\r
1546\r
1547 var message;\r
1548 if (!result) {\r
1549 if (this.message) {\r
1550 message = this.message.apply(this, arguments);\r
1551 if (jasmine.isArray_(message)) {\r
1552 message = message[this.isNot ? 1 : 0];\r
1553 }\r
1554 } else {\r
1555 var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); });\r
1556 message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate;\r
1557 if (matcherArgs.length > 0) {\r
1558 for (var i = 0; i < matcherArgs.length; i++) {\r
1559 if (i > 0) message += ",";\r
1560 message += " " + jasmine.pp(matcherArgs[i]);\r
1561 }\r
1562 }\r
1563 message += ".";\r
1564 }\r
1565 }\r
1566 var expectationResult = new jasmine.ExpectationResult({\r
1567 matcherName: matcherName,\r
1568 passed: result,\r
1569 expected: matcherArgs.length > 1 ? matcherArgs : matcherArgs[0],\r
1570 actual: this.actual,\r
1571 message: message\r
1572 });\r
1573 this.spec.addMatcherResult(expectationResult);\r
1574 return jasmine.undefined;\r
1575 };\r
1576};\r
1577\r
1578\r
1579\r
1580\r
1581/**\r
1582 * toBe: compares the actual to the expected using ===\r
1583 * @param expected\r
1584 */\r
1585jasmine.Matchers.prototype.toBe = function(expected) {\r
1586 return this.actual === expected;\r
1587};\r
1588\r
1589/**\r
1590 * toNotBe: compares the actual to the expected using !==\r
1591 * @param expected\r
1592 * @deprecated as of 1.0. Use not.toBe() instead.\r
1593 */\r
1594jasmine.Matchers.prototype.toNotBe = function(expected) {\r
1595 return this.actual !== expected;\r
1596};\r
1597\r
1598/**\r
1599 * toEqual: compares the actual to the expected using common sense equality. Handles Objects, Arrays, etc.\r
1600 *\r
1601 * @param expected\r
1602 */\r
1603jasmine.Matchers.prototype.toEqual = function(expected) {\r
1604 return this.env.equals_(this.actual, expected);\r
1605};\r
1606\r
1607/**\r
1608 * toNotEqual: compares the actual to the expected using the ! of jasmine.Matchers.toEqual\r
1609 * @param expected\r
1610 * @deprecated as of 1.0. Use not.toEqual() instead.\r
1611 */\r
1612jasmine.Matchers.prototype.toNotEqual = function(expected) {\r
1613 return !this.env.equals_(this.actual, expected);\r
1614};\r
1615\r
1616/**\r
1617 * Matcher that compares the actual to the expected using a regular expression. Constructs a RegExp, so takes\r
1618 * a pattern or a String.\r
1619 *\r
1620 * @param expected\r
1621 */\r
1622jasmine.Matchers.prototype.toMatch = function(expected) {\r
1623 return new RegExp(expected).test(this.actual);\r
1624};\r
1625\r
1626/**\r
1627 * Matcher that compares the actual to the expected using the boolean inverse of jasmine.Matchers.toMatch\r
1628 * @param expected\r
1629 * @deprecated as of 1.0. Use not.toMatch() instead.\r
1630 */\r
1631jasmine.Matchers.prototype.toNotMatch = function(expected) {\r
1632 return !(new RegExp(expected).test(this.actual));\r
1633};\r
1634\r
1635/**\r
1636 * Matcher that compares the actual to jasmine.undefined.\r
1637 */\r
1638jasmine.Matchers.prototype.toBeDefined = function() {\r
1639 return (this.actual !== jasmine.undefined);\r
1640};\r
1641\r
1642/**\r
1643 * Matcher that compares the actual to jasmine.undefined.\r
1644 */\r
1645jasmine.Matchers.prototype.toBeUndefined = function() {\r
1646 return (this.actual === jasmine.undefined);\r
1647};\r
1648\r
1649/**\r
1650 * Matcher that compares the actual to null.\r
1651 */\r
1652jasmine.Matchers.prototype.toBeNull = function() {\r
1653 return (this.actual === null);\r
1654};\r
1655\r
1656/**\r
1657 * Matcher that compares the actual to NaN.\r
1658 */\r
1659jasmine.Matchers.prototype.toBeNaN = function() {\r
1660 this.message = function() {\r
1661 return [ "Expected " + jasmine.pp(this.actual) + " to be NaN." ];\r
1662 };\r
1663\r
1664 return (this.actual !== this.actual);\r
1665};\r
1666\r
1667/**\r
1668 * Matcher that boolean not-nots the actual.\r
1669 */\r
1670jasmine.Matchers.prototype.toBeTruthy = function() {\r
1671 return !!this.actual;\r
1672};\r
1673\r
1674\r
1675/**\r
1676 * Matcher that boolean nots the actual.\r
1677 */\r
1678jasmine.Matchers.prototype.toBeFalsy = function() {\r
1679 return !this.actual;\r
1680};\r
1681\r
1682\r
1683/**\r
1684 * Matcher that checks to see if the actual, a Jasmine spy, was called.\r
1685 */\r
1686jasmine.Matchers.prototype.toHaveBeenCalled = function() {\r
1687 if (arguments.length > 0) {\r
1688 throw new Error('toHaveBeenCalled does not take arguments, use toHaveBeenCalledWith');\r
1689 }\r
1690\r
1691 if (!jasmine.isSpy(this.actual)) {\r
1692 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1693 }\r
1694\r
1695 this.message = function() {\r
1696 return [\r
1697 "Expected spy " + this.actual.identity + " to have been called.",\r
1698 "Expected spy " + this.actual.identity + " not to have been called."\r
1699 ];\r
1700 };\r
1701\r
1702 return this.actual.wasCalled;\r
1703};\r
1704\r
1705/** @deprecated Use expect(xxx).toHaveBeenCalled() instead */\r
1706jasmine.Matchers.prototype.wasCalled = jasmine.Matchers.prototype.toHaveBeenCalled;\r
1707\r
1708/**\r
1709 * Matcher that checks to see if the actual, a Jasmine spy, was not called.\r
1710 *\r
1711 * @deprecated Use expect(xxx).not.toHaveBeenCalled() instead\r
1712 */\r
1713jasmine.Matchers.prototype.wasNotCalled = function() {\r
1714 if (arguments.length > 0) {\r
1715 throw new Error('wasNotCalled does not take arguments');\r
1716 }\r
1717\r
1718 if (!jasmine.isSpy(this.actual)) {\r
1719 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1720 }\r
1721\r
1722 this.message = function() {\r
1723 return [\r
1724 "Expected spy " + this.actual.identity + " to not have been called.",\r
1725 "Expected spy " + this.actual.identity + " to have been called."\r
1726 ];\r
1727 };\r
1728\r
1729 return !this.actual.wasCalled;\r
1730};\r
1731\r
1732/**\r
1733 * Matcher that checks to see if the actual, a Jasmine spy, was called with a set of parameters.\r
1734 *\r
1735 * @example\r
1736 *\r
1737 */\r
1738jasmine.Matchers.prototype.toHaveBeenCalledWith = function() {\r
1739 var expectedArgs = jasmine.util.argsToArray(arguments);\r
1740 if (!jasmine.isSpy(this.actual)) {\r
1741 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1742 }\r
1743 this.message = function() {\r
1744 var invertedMessage = "Expected spy " + this.actual.identity + " not to have been called with " + jasmine.pp(expectedArgs) + " but it was.";\r
1745 var positiveMessage = "";\r
1746 if (this.actual.callCount === 0) {\r
1747 positiveMessage = "Expected spy " + this.actual.identity + " to have been called with " + jasmine.pp(expectedArgs) + " but it was never called.";\r
1748 } else {\r
1749 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
1750 }\r
1751 return [positiveMessage, invertedMessage];\r
1752 };\r
1753\r
1754 return this.env.contains_(this.actual.argsForCall, expectedArgs);\r
1755};\r
1756\r
1757/** @deprecated Use expect(xxx).toHaveBeenCalledWith() instead */\r
1758jasmine.Matchers.prototype.wasCalledWith = jasmine.Matchers.prototype.toHaveBeenCalledWith;\r
1759\r
1760/** @deprecated Use expect(xxx).not.toHaveBeenCalledWith() instead */\r
1761jasmine.Matchers.prototype.wasNotCalledWith = function() {\r
1762 var expectedArgs = jasmine.util.argsToArray(arguments);\r
1763 if (!jasmine.isSpy(this.actual)) {\r
1764 throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');\r
1765 }\r
1766\r
1767 this.message = function() {\r
1768 return [\r
1769 "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was",\r
1770 "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was"\r
1771 ];\r
1772 };\r
1773\r
1774 return !this.env.contains_(this.actual.argsForCall, expectedArgs);\r
1775};\r
1776\r
1777/**\r
1778 * Matcher that checks that the expected item is an element in the actual Array.\r
1779 *\r
1780 * @param {Object} expected\r
1781 */\r
1782jasmine.Matchers.prototype.toContain = function(expected) {\r
1783 return this.env.contains_(this.actual, expected);\r
1784};\r
1785\r
1786/**\r
1787 * Matcher that checks that the expected item is NOT an element in the actual Array.\r
1788 *\r
1789 * @param {Object} expected\r
1790 * @deprecated as of 1.0. Use not.toContain() instead.\r
1791 */\r
1792jasmine.Matchers.prototype.toNotContain = function(expected) {\r
1793 return !this.env.contains_(this.actual, expected);\r
1794};\r
1795\r
1796jasmine.Matchers.prototype.toBeLessThan = function(expected) {\r
1797 return this.actual < expected;\r
1798};\r
1799\r
1800jasmine.Matchers.prototype.toBeGreaterThan = function(expected) {\r
1801 return this.actual > expected;\r
1802};\r
1803\r
1804/**\r
1805 * Matcher that checks that the expected item is equal to the actual item\r
1806 * up to a given level of decimal precision (default 2).\r
1807 *\r
1808 * @param {Number} expected\r
1809 * @param {Number} precision, as number of decimal places\r
1810 */\r
1811jasmine.Matchers.prototype.toBeCloseTo = function(expected, precision) {\r
1812 if (!(precision === 0)) {\r
1813 precision = precision || 2;\r
1814 }\r
1815 return Math.abs(expected - this.actual) < (Math.pow(10, -precision) / 2);\r
1816};\r
1817\r
1818/**\r
1819 * Matcher that checks that the expected exception was thrown by the actual.\r
1820 *\r
1821 * @param {String} [expected]\r
1822 */\r
1823jasmine.Matchers.prototype.toThrow = function(expected) {\r
1824 var result = false;\r
1825 var exception;\r
1826 if (typeof this.actual != 'function') {\r
1827 throw new Error('Actual is not a function');\r
1828 }\r
1829 try {\r
1830 this.actual();\r
1831 } catch (e) {\r
1832 exception = e;\r
1833 }\r
1834 if (exception) {\r
1835 result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));\r
1836 }\r
1837\r
1838 var not = this.isNot ? "not " : "";\r
1839\r
1840 this.message = function() {\r
1841 if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) {\r
1842 return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');\r
1843 } else {\r
1844 return "Expected function to throw an exception.";\r
1845 }\r
1846 };\r
1847\r
1848 return result;\r
1849};\r
1850\r
1851jasmine.Matchers.Any = function(expectedClass) {\r
1852 this.expectedClass = expectedClass;\r
1853};\r
1854\r
1855jasmine.Matchers.Any.prototype.jasmineMatches = function(other) {\r
1856 if (this.expectedClass == String) {\r
1857 return typeof other == 'string' || other instanceof String;\r
1858 }\r
1859\r
1860 if (this.expectedClass == Number) {\r
1861 return typeof other == 'number' || other instanceof Number;\r
1862 }\r
1863\r
1864 if (this.expectedClass == Function) {\r
1865 return typeof other == 'function' || other instanceof Function;\r
1866 }\r
1867\r
1868 if (this.expectedClass == Object) {\r
1869 return typeof other == 'object';\r
1870 }\r
1871\r
1872 return other instanceof this.expectedClass;\r
1873};\r
1874\r
1875jasmine.Matchers.Any.prototype.jasmineToString = function() {\r
1876 return '<jasmine.any(' + this.expectedClass + ')>';\r
1877};\r
1878\r
1879jasmine.Matchers.ObjectContaining = function (sample) {\r
1880 this.sample = sample;\r
1881};\r
1882\r
1883jasmine.Matchers.ObjectContaining.prototype.jasmineMatches = function(other, mismatchKeys, mismatchValues) {\r
1884 mismatchKeys = mismatchKeys || [];\r
1885 mismatchValues = mismatchValues || [];\r
1886\r
1887 var env = jasmine.getEnv();\r
1888\r
1889 var hasKey = function(obj, keyName) {\r
1890 return obj != null && obj[keyName] !== jasmine.undefined;\r
1891 };\r
1892\r
1893 for (var property in this.sample) {\r
1894 if (!hasKey(other, property) && hasKey(this.sample, property)) {\r
1895 mismatchKeys.push("expected has key '" + property + "', but missing from actual.");\r
1896 }\r
1897 else if (!env.equals_(this.sample[property], other[property], mismatchKeys, mismatchValues)) {\r
1898 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
1899 }\r
1900 }\r
1901\r
1902 return (mismatchKeys.length === 0 && mismatchValues.length === 0);\r
1903};\r
1904\r
1905jasmine.Matchers.ObjectContaining.prototype.jasmineToString = function () {\r
1906 return "<jasmine.objectContaining(" + jasmine.pp(this.sample) + ")>";\r
1907};\r
1908/**\r
1909 * Override of toThrow special opera 10 !!! \r
1910 */\r
1911jasmine.Matchers.prototype.toThrow = function(expected) {\r
1912 var result = false;\r
1913 var exception;\r
1914 if (typeof this.actual != 'function') {\r
1915 throw new Error('Actual is not a function');\r
1916 }\r
1917 try {\r
1918 this.actual();\r
1919 } catch (e) {\r
1920 exception = e;\r
1921 }\r
1922 if (exception) {\r
1923 result = (expected === jasmine.undefined || this.env.contains_(exception.message || exception, expected.message || expected));\r
1924 }\r
1925\r
1926 var not = this.isNot ? "not " : "";\r
1927\r
1928 this.message = function() {\r
1929 if (exception && (expected === jasmine.undefined || !this.env.contains_(exception.message || exception, expected.message || expected))) {\r
1930 return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", exception.message || exception].join(' ');\r
1931 } else {\r
1932 return "Expected function to throw an exception.";\r
1933 }\r
1934 };\r
1935\r
1936 return result;\r
1937};\r
1938\r
1939jasmine.Matchers.prototype.toRaiseExtError = function(expected) {\r
1940 var result = false,\r
1941 global = Ext.global,\r
1942 extError;\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 Ext.global = {\r
1949 console: {\r
1950 dir: function(s) {\r
1951 return s;\r
1952 },\r
1953 log: function(s) {\r
1954 return s;\r
1955 },\r
1956 error: function(s) {\r
1957 return s;\r
1958 },\r
1959 warn: function(s) {\r
1960 return s;\r
1961 }\r
1962 }\r
1963 };\r
1964 \r
1965 try {\r
1966 this.actual();\r
1967 } catch (e) {\r
1968 extError = e;\r
1969 }\r
1970\r
1971 \r
1972 Ext.global = global;\r
1973\r
1974 if (extError && extError instanceof Error) {\r
1975 result = (expected === jasmine.undefined || this.env.contains_(extError.toString(), expected.message || expected));\r
1976 }\r
1977\r
1978 \r
1979 var not = this.isNot ? "not " : "";\r
1980\r
1981 this.message = function() {\r
1982 if (!extError instanceof Error) {\r
1983 return "Exception thrown is not an instance of Ext.Error";\r
1984 } else if (extError && (expected === jasmine.undefined || !this.env.contains_(extError.toString(), expected.message || expected))) {\r
1985 return ["Expected function " + not + "to throw", expected ? expected.message || expected : " an exception", ", but it threw", extError.toString()].join(' ');\r
1986 } else {\r
1987 return "Expected function to throw an exception.";\r
1988 }\r
1989 };\r
1990\r
1991 return result;\r
1992};\r
1993\r
1994jasmine.Matchers.prototype.hasHTML = function(expected) {\r
1995 var me = this;\r
1996 \r
1997 if (!me.actual || !me.actual.tagName) {\r
1998 throw new Error('Actual is not a dom element');\r
1999 }\r
2000 if (jasmine.browser.isSafari3) {\r
2001 expected = expected.replace(/&gt;/g, '>');\r
2002 }\r
2003 // this normalize innerHTML which could vary a lot\r
2004 var normalizedHTML = me.actual.innerHTML.replace(/<[^>]*>/g, function(match1) {\r
2005 return match1.toLowerCase().replace(/=\w+/g, function(match2) { \r
2006 return '="' + match2.split('=')[1] + '"'; \r
2007 });\r
2008 });\r
2009\r
2010 me.message = function() {\r
2011 return [\r
2012 "Expected dom element innerHTML to be " + expected + " but was " + normalizedHTML,\r
2013 "Expected dom element innerHTML to not be " + expected + "."\r
2014 ];\r
2015 };\r
2016 \r
2017 return normalizedHTML === expected;\r
2018};\r
2019\r
2020jasmine.Matchers.prototype.toHaveCls = function(cls) {\r
2021 return Ext.fly(this.actual).hasCls(cls);\r
2022};\r
2023\r
2024jasmine.Matchers.prototype.toEqualTime = function(hour, minute, second, ms) {\r
2025 var actual = this.actual;\r
2026 return actual instanceof Date &&\r
2027 actual.getHours() === hour &&\r
2028 actual.getMinutes() === (minute || 0) &&\r
2029 actual.getSeconds() === (second || 0) &&\r
2030 actual.getMilliseconds() === (ms || 0);\r
2031\r
2032};\r
2033\r
2034jasmine.Matchers.prototype.toBePositionedAt = function(x, y) {\r
2035 var xy = this.actual.getXY();\r
2036 this.message = function() {\r
2037 return "Expected Ext.Element to be positioned at (" + x + "," + y + ") but was positioned at (" + xy[0] + "," + xy[1] + ")";\r
2038 };\r
2039 return xy[0] === x && xy[1] === y;\r
2040};\r
2041\r
2042(function () {\r
2043 var elementPropGetters = {\r
2044 x: function (el, root) {\r
2045 var x = el.getX(),\r
2046 x0 = root ? root.el.getX() : el.getX();\r
2047 return x - x0;\r
2048 },\r
2049 y: function (el, root) {\r
2050 var y = el.getY(),\r
2051 y0 = root ? root.el.getY() : el.getY();\r
2052 return y - y0;\r
2053 },\r
2054 w: function (el) {\r
2055 return el.getWidth();\r
2056 },\r
2057 h: function (el) {\r
2058 return el.getHeight();\r
2059 },\r
2060 xywh: function(el, root) {\r
2061 var x= el.getX(),\r
2062 x0 = root ? root.el.getX() : el.getX(),\r
2063 y = el.getY(),\r
2064 y0 = root ? root.el.getY() : el.getY(),\r
2065 w = el.getWidth(),\r
2066 h = el.getHeight(),\r
2067 dims = [];\r
2068 dims.push(x - x0, y - y0, w, h);\r
2069 return dims.join(' ');\r
2070 },\r
2071 cls: function (el) {\r
2072 return el.dom.className;\r
2073 }\r
2074 },\r
2075 browsers = [\r
2076 "IE6", "IE7", "IE8", "IE9", "IE",\r
2077 "Gecko3", "Gecko4", "Gecko5", "Gecko10", "Gecko",\r
2078 "FF3_6", "FF4", "FF5",\r
2079 "Chrome",\r
2080 "Safari2", "Safari3", "Safari4", "Safari5", "Safari"\r
2081 ],\r
2082 blen = browsers.length,\r
2083 b, browser,\r
2084 browserCheck = function(expected){\r
2085 if(Ext.isNumeric(expected) || Ext.isArray(expected)) {\r
2086 return expected;\r
2087 }\r
2088 for (b = 0; b < blen; b++) {\r
2089 browser = browsers[b];\r
2090 if (expected.hasOwnProperty(browser) && Ext["is" + browser]){\r
2091 return expected[browser];\r
2092 }\r
2093 }\r
2094 return expected['*'] || expected;\r
2095 };\r
2096\r
2097\r
2098 function checkLayout (comp, layout, root, path) {\r
2099 Ext.Object.each(layout, function (name, value) {\r
2100 if (name == 'items' || name == 'dockedItems') {\r
2101 Ext.Object.each(value, function (id, sub) {\r
2102 var isNum = String(parseInt(id,10)) == id,\r
2103 child = isNum ? comp[name].items[parseInt(id,10)]\r
2104 : (comp.getComponent(id) || comp.child(id));\r
2105\r
2106 if (isNum) {\r
2107 id = '.' + name + '[' + id + ']';\r
2108 } else if (id.charAt(0) != ':') {\r
2109 id = '_' + id;\r
2110 }\r
2111\r
2112 if (child) {\r
2113 checkLayout(child, sub, comp, path + id);\r
2114 } else {\r
2115 expect(id).toBe('found!');\r
2116 }\r
2117 });\r
2118 } else {\r
2119 // the name is an element name like 'body'\r
2120 var el = comp[name];\r
2121\r
2122 if (!el) {\r
2123 // no child el matched, assume the key is a CSS selector\r
2124 el = comp.el.selectNode(name, false);\r
2125 }\r
2126\r
2127 if (el.isComponent) {\r
2128 checkLayout(el, value, el.ownerCt, path + '_' + name);\r
2129 } else if (el.dom) {\r
2130 value = browserCheck(value);\r
2131 if (value.xywh) {\r
2132 var dims = value.xywh.split(' ');\r
2133 value.x = eval('(' + dims[0] + ')');\r
2134 value.y = eval('(' + dims[1] + ')');\r
2135 value.w = eval('(' + dims[2] + ')');\r
2136 value.h = eval('(' + dims[3] + ')');\r
2137 delete value.xywh;\r
2138 }\r
2139 Ext.Object.each(value, function (prop, expected) {\r
2140 var actual = elementPropGetters[prop](el, root || comp.el),\r
2141 pfx = path + '.' + name + '.' + prop + '=';\r
2142\r
2143 if (Ext.isArray(expected)) {\r
2144 if (actual < expected[0] || actual > expected[1]) {\r
2145 expect(pfx + '=' + actual).\r
2146 toBe('in [' + expected[0] + ',' + expected[1] + ']');\r
2147 }\r
2148 } else if (actual != expected) {\r
2149 expect(pfx + actual).toEqual(expected);\r
2150 }\r
2151 });\r
2152 }\r
2153 }\r
2154 });\r
2155 }\r
2156\r
2157 jasmine.Matchers.prototype.toHaveLayout = function(expected) {\r
2158 var comp = this.actual;\r
2159 checkLayout(comp, expected, comp.ownerCt, comp.getXType());\r
2160 return true;\r
2161 };\r
2162\r
2163 jasmine.Matchers.prototype.toBeLessThanOrEqual = function(expected) {\r
2164 return this.actual <= expected;\r
2165 };\r
2166\r
2167 jasmine.Matchers.prototype.toBeGreaterThanOrEqual = function(expected) {\r
2168 return this.actual >= expected;\r
2169 };\r
2170\r
2171})();\r
2172\r
2173\r
2174 jasmine.Matchers.prototype.toHaveFiredEvents = function() {\r
2175 var calls = this.actual.fireEvent.calls,\r
2176 i = 0,\r
2177 ret = true,\r
2178 expectedEvents = Array.prototype.slice.call(arguments, 0),\r
2179 length = expectedEvents.length,\r
2180 actualEvents = [], \r
2181 actualEvent,\r
2182 expectedEvent;\r
2183\r
2184\r
2185 \r
2186 for (;i < length; i++) {\r
2187 expectedEvent = expectedEvents[i];\r
2188 try {\r
2189 actualEvent = calls[i].args[0];\r
2190 } catch (e) {\r
2191 actualEvent = null;\r
2192 }\r
2193 if (actualEvent) {\r
2194 actualEvents.push(actualEvent);\r
2195 }\r
2196\r
2197 if (actualEvent != expectedEvent) {\r
2198 ret = false;\r
2199 }\r
2200 }\r
2201 \r
2202 this.message = function() {\r
2203 return "Expected events flow to be (" + expectedEvents.length + " events): \n" + expectedEvents.join('\n') + "\nBut it was (" + actualEvents.length + " events): \n"+ actualEvents.join('\n');\r
2204 };\r
2205 return ret;\r
2206 };/**\r
2207 * @constructor\r
2208 */\r
2209jasmine.MultiReporter = function() {\r
2210 this.subReporters_ = [];\r
2211};\r
2212jasmine.util.inherit(jasmine.MultiReporter, jasmine.Reporter);\r
2213\r
2214jasmine.MultiReporter.prototype.addReporter = function(reporter) {\r
2215 this.subReporters_.push(reporter);\r
2216};\r
2217\r
2218(function() {\r
2219 var functionNames = [\r
2220 "reportRunnerStarting",\r
2221 "reportRunnerResults",\r
2222 "reportSuiteStarting",\r
2223 "reportSuiteResults",\r
2224 "reportSpecStarting",\r
2225 "reportSpecResults",\r
2226 "log"\r
2227 ];\r
2228 for (var i = 0; i < functionNames.length; i++) {\r
2229 var functionName = functionNames[i];\r
2230 jasmine.MultiReporter.prototype[functionName] = (function(functionName) {\r
2231 return function() {\r
2232 for (var j = 0; j < this.subReporters_.length; j++) {\r
2233 var subReporter = this.subReporters_[j];\r
2234 if (subReporter[functionName]) {\r
2235 subReporter[functionName].apply(subReporter, arguments);\r
2236 }\r
2237 }\r
2238 };\r
2239 })(functionName);\r
2240 }\r
2241})();/**\r
2242 * Holds results for a set of Jasmine spec. Allows for the results array to hold another jasmine.NestedResults\r
2243 *\r
2244 * @constructor\r
2245 */\r
2246jasmine.NestedResults = function() {\r
2247 /**\r
2248 * The total count of results\r
2249 */\r
2250 this.totalCount = 0;\r
2251 /**\r
2252 * Number of passed results\r
2253 */\r
2254 this.passedCount = 0;\r
2255 /**\r
2256 * Number of failed results\r
2257 */\r
2258 this.failedCount = 0;\r
2259 /**\r
2260 * Was this suite/spec skipped?\r
2261 */\r
2262 this.skipped = false;\r
2263 /**\r
2264 * @ignore\r
2265 */\r
2266 this.items_ = [];\r
2267};\r
2268\r
2269/**\r
2270 * Roll up the result counts.\r
2271 *\r
2272 * @param result\r
2273 */\r
2274jasmine.NestedResults.prototype.rollupCounts = function(result) {\r
2275 this.totalCount += result.totalCount;\r
2276 this.passedCount += result.passedCount;\r
2277 this.failedCount += result.failedCount;\r
2278};\r
2279\r
2280/**\r
2281 * Adds a log message.\r
2282 * @param values Array of message parts which will be concatenated later.\r
2283 */\r
2284jasmine.NestedResults.prototype.log = function(values) {\r
2285 this.items_.push(new jasmine.MessageResult(values));\r
2286};\r
2287\r
2288/**\r
2289 * Getter for the results: message & results.\r
2290 */\r
2291jasmine.NestedResults.prototype.getItems = function() {\r
2292 return this.items_;\r
2293};\r
2294\r
2295/**\r
2296 * Adds a result, tracking counts (total, passed, & failed)\r
2297 * @param {jasmine.ExpectationResult|jasmine.NestedResults} result\r
2298 */\r
2299jasmine.NestedResults.prototype.addResult = function(result) {\r
2300 if (result.type != 'log') {\r
2301 if (result.items_) {\r
2302 this.rollupCounts(result);\r
2303 } else {\r
2304 this.totalCount++;\r
2305 if (result.passed()) {\r
2306 this.passedCount++;\r
2307 } else {\r
2308 this.failedCount++;\r
2309 }\r
2310 }\r
2311 }\r
2312 this.items_.push(result);\r
2313};\r
2314\r
2315/**\r
2316 * @returns {Boolean} True if <b>everything</b> below passed\r
2317 */\r
2318jasmine.NestedResults.prototype.passed = function() {\r
2319 return this.passedCount === this.totalCount;\r
2320};\r
2321/**\r
2322 * Base class for pretty printing for expectation results.\r
2323 */\r
2324jasmine.PrettyPrinter = function() {\r
2325 this.ppNestLevel_ = 0;\r
2326};\r
2327\r
2328/**\r
2329 * Formats a value in a nice, human-readable string.\r
2330 *\r
2331 * @param value\r
2332 */\r
2333jasmine.PrettyPrinter.prototype.format = function(value) {\r
2334 this.ppNestLevel_++;\r
2335 try {\r
2336 if (value === jasmine.undefined) {\r
2337 this.emitScalar('undefined');\r
2338 } else if (value === null) {\r
2339 this.emitScalar('null');\r
2340 } else if (value === jasmine.getGlobal()) {\r
2341 this.emitScalar('<global>');\r
2342 } else if (value.jasmineToString) {\r
2343 this.emitScalar(value.jasmineToString());\r
2344 } else if (typeof value === 'string') {\r
2345 this.emitString(value);\r
2346 } else if (jasmine.isSpy(value)) {\r
2347 this.emitScalar("spy on " + value.identity);\r
2348 } else if (value instanceof RegExp) {\r
2349 this.emitScalar(value.toString());\r
2350 } else if (typeof value === 'function') {\r
2351 this.emitScalar('Function');\r
2352 } else if (typeof value.nodeType === 'number') {\r
2353 this.emitScalar('HTMLNode');\r
2354 } else if (value instanceof Date) {\r
2355 this.emitScalar('Date(' + value + ')');\r
2356 } else if (value.__Jasmine_been_here_before__) {\r
2357 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');\r
2358 } else if (jasmine.isArray_(value) || typeof value == 'object') {\r
2359 value.__Jasmine_been_here_before__ = true;\r
2360 if (jasmine.isArray_(value)) {\r
2361 this.emitArray(value);\r
2362 } else {\r
2363 this.emitObject(value);\r
2364 }\r
2365 delete value.__Jasmine_been_here_before__;\r
2366 } else {\r
2367 this.emitScalar(value.toString());\r
2368 }\r
2369 } finally {\r
2370 this.ppNestLevel_--;\r
2371 }\r
2372};\r
2373\r
2374jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {\r
2375 for (var property in obj) {\r
2376 if (!obj.hasOwnProperty(property)) continue;\r
2377 if (property == '__Jasmine_been_here_before__') continue;\r
2378 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && \r
2379 obj.__lookupGetter__(property) !== null) : false);\r
2380 }\r
2381};\r
2382\r
2383jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;\r
2384jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;\r
2385jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;\r
2386jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;\r
2387\r
2388jasmine.StringPrettyPrinter = function() {\r
2389 jasmine.PrettyPrinter.call(this);\r
2390\r
2391 this.string = '';\r
2392};\r
2393jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);\r
2394\r
2395jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {\r
2396 this.append(value);\r
2397};\r
2398\r
2399jasmine.StringPrettyPrinter.prototype.emitString = function(value) {\r
2400 this.append("'" + value + "'");\r
2401};\r
2402\r
2403jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {\r
2404 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
2405 this.append("Array");\r
2406 return;\r
2407 }\r
2408\r
2409 this.append('[ ');\r
2410 for (var i = 0; i < array.length; i++) {\r
2411 if (i > 0) {\r
2412 this.append(', ');\r
2413 }\r
2414 this.format(array[i]);\r
2415 }\r
2416 this.append(' ]');\r
2417};\r
2418\r
2419jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {\r
2420 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
2421 this.append("Object");\r
2422 return;\r
2423 }\r
2424\r
2425 var self = this;\r
2426 this.append('{ ');\r
2427 var first = true;\r
2428\r
2429 this.iterateObject(obj, function(property, isGetter) {\r
2430 if (first) {\r
2431 first = false;\r
2432 } else {\r
2433 self.append(', ');\r
2434 }\r
2435\r
2436 self.append(property);\r
2437 self.append(' : ');\r
2438 if (isGetter) {\r
2439 self.append('<getter>');\r
2440 } else {\r
2441 self.format(obj[property]);\r
2442 }\r
2443 });\r
2444\r
2445 this.append(' }');\r
2446};\r
2447\r
2448jasmine.StringPrettyPrinter.prototype.append = function(value) {\r
2449 this.string += value;\r
2450};\r
2451(function() {\r
2452 var prototype = jasmine.PrettyPrinter.prototype,\r
2453 superFormat = prototype.format;\r
2454\r
2455 prototype.format = function(value) {\r
2456 if (value && value.$className) {\r
2457 // support for pretty printing instances of Ext classes\r
2458 this.emitScalar(value.$className + '#' + value.id);\r
2459 } else {\r
2460 superFormat.call(this, value);\r
2461 }\r
2462 };\r
2463})();jasmine.Queue = function(env) {\r
2464 this.env = env;\r
2465\r
2466 // parallel to blocks. each true value in this array means the block will\r
2467 // get executed even if we abort\r
2468 this.ensured = [];\r
2469 this.blocks = [];\r
2470 this.running = false;\r
2471 this.index = 0;\r
2472 this.offset = 0;\r
2473 this.abort = false;\r
2474};\r
2475\r
2476jasmine.Queue.prototype.addBefore = function(block, ensure) {\r
2477 if (ensure === jasmine.undefined) {\r
2478 ensure = false;\r
2479 }\r
2480\r
2481 this.blocks.unshift(block);\r
2482 this.ensured.unshift(ensure);\r
2483};\r
2484\r
2485jasmine.Queue.prototype.add = function(block, ensure) {\r
2486 if (ensure === jasmine.undefined) {\r
2487 ensure = false;\r
2488 }\r
2489\r
2490 this.blocks.push(block);\r
2491 this.ensured.push(ensure);\r
2492};\r
2493\r
2494jasmine.Queue.prototype.insertNext = function(block, ensure) {\r
2495 if (ensure === jasmine.undefined) {\r
2496 ensure = false;\r
2497 }\r
2498\r
2499 this.ensured.splice((this.index + this.offset + 1), 0, ensure);\r
2500 this.blocks.splice((this.index + this.offset + 1), 0, block);\r
2501 this.offset++;\r
2502};\r
2503\r
2504jasmine.Queue.prototype.start = function(onComplete) {\r
2505 this.running = true;\r
2506 this.onComplete = onComplete;\r
2507 this.next_();\r
2508};\r
2509\r
2510jasmine.Queue.prototype.isRunning = function() {\r
2511 return this.running;\r
2512};\r
2513\r
2514jasmine.Queue.LOOP_DONT_RECURSE = true;\r
2515\r
2516jasmine.Queue.prototype.next_ = function() {\r
2517 var self = this;\r
2518 var goAgain = true;\r
2519\r
2520 while (goAgain) {\r
2521 goAgain = false;\r
2522 \r
2523 if (self.index < self.blocks.length && !(this.abort && !this.ensured[self.index])) {\r
2524 var calledSynchronously = true;\r
2525 var completedSynchronously = false;\r
2526\r
2527 var onComplete = function () {\r
2528 if (jasmine.Queue.LOOP_DONT_RECURSE && calledSynchronously) {\r
2529 completedSynchronously = true;\r
2530 return;\r
2531 }\r
2532\r
2533 if (self.blocks[self.index].abort) {\r
2534 self.abort = true;\r
2535 }\r
2536\r
2537 self.offset = 0;\r
2538 self.index++;\r
2539\r
2540 var now = new Date().getTime();\r
2541 if (self.env.updateInterval && now - self.env.lastUpdate > self.env.updateInterval) {\r
2542 self.env.lastUpdate = now;\r
2543 self.env.setTimeout(function() {\r
2544 self.next_();\r
2545 }, 0);\r
2546 } else {\r
2547 if (jasmine.Queue.LOOP_DONT_RECURSE && completedSynchronously) {\r
2548 goAgain = true;\r
2549 } else {\r
2550 self.next_();\r
2551 }\r
2552 }\r
2553 };\r
2554 self.blocks[self.index].execute(onComplete);\r
2555\r
2556 calledSynchronously = false;\r
2557 if (completedSynchronously) {\r
2558 onComplete();\r
2559 }\r
2560 \r
2561 } else {\r
2562 self.running = false;\r
2563 if (self.onComplete) {\r
2564 self.onComplete();\r
2565 }\r
2566 }\r
2567 }\r
2568};\r
2569\r
2570jasmine.Queue.prototype.results = function() {\r
2571 var results = new jasmine.NestedResults();\r
2572 for (var i = 0; i < this.blocks.length; i++) {\r
2573 if (this.blocks[i].results) {\r
2574 results.addResult(this.blocks[i].results());\r
2575 }\r
2576 }\r
2577 return results;\r
2578};\r
2579\r
2580\r
2581/**\r
2582 * Runner\r
2583 *\r
2584 * @constructor\r
2585 * @param {jasmine.Env} env\r
2586 */\r
2587jasmine.Runner = function(env) {\r
2588 var self = this;\r
2589 self.env = env;\r
2590 self.queue = new jasmine.Queue(env);\r
2591 self.before_ = [];\r
2592 self.after_ = [];\r
2593 self.suites_ = [];\r
2594};\r
2595\r
2596jasmine.Runner.prototype.execute = function() {\r
2597 var self = this;\r
2598 if (self.env.reporter.reportRunnerStarting) {\r
2599 self.env.reporter.reportRunnerStarting(this);\r
2600 }\r
2601 self.queue.start(function () {\r
2602 self.finishCallback();\r
2603 });\r
2604};\r
2605\r
2606jasmine.Runner.prototype.beforeEach = function(beforeEachFunction) {\r
2607 beforeEachFunction.typeName = 'beforeEach';\r
2608 this.before_.splice(0,0,beforeEachFunction);\r
2609};\r
2610\r
2611jasmine.Runner.prototype.afterEach = function(afterEachFunction) {\r
2612 afterEachFunction.typeName = 'afterEach';\r
2613 this.after_.splice(0,0,afterEachFunction);\r
2614};\r
2615\r
2616\r
2617jasmine.Runner.prototype.finishCallback = function() {\r
2618 this.env.reporter.reportRunnerResults(this);\r
2619};\r
2620\r
2621jasmine.Runner.prototype.addSuite = function(suite) {\r
2622 this.suites_.push(suite);\r
2623};\r
2624\r
2625jasmine.Runner.prototype.add = function(block) {\r
2626 if (block instanceof jasmine.Suite) {\r
2627 this.addSuite(block);\r
2628 }\r
2629 this.queue.add(block);\r
2630};\r
2631\r
2632jasmine.Runner.prototype.specs = function () {\r
2633 var suites = this.suites();\r
2634 var specs = [];\r
2635 for (var i = 0; i < suites.length; i++) {\r
2636 specs = specs.concat(suites[i].specs());\r
2637 }\r
2638 return specs;\r
2639};\r
2640\r
2641jasmine.Runner.prototype.suites = function() {\r
2642 return this.suites_;\r
2643};\r
2644\r
2645jasmine.Runner.prototype.topLevelSuites = function() {\r
2646 var topLevelSuites = [];\r
2647 for (var i = 0; i < this.suites_.length; i++) {\r
2648 if (!this.suites_[i].parentSuite) {\r
2649 topLevelSuites.push(this.suites_[i]);\r
2650 }\r
2651 }\r
2652 return topLevelSuites;\r
2653};\r
2654\r
2655jasmine.Runner.prototype.results = function() {\r
2656 return this.queue.results();\r
2657};\r
2658jasmine.Runner.prototype.filter = function (suiteIds, specIds) {\r
2659 // convert [1, 2] into { 1: true, 2: true }\r
2660 //\r
2661 if (typeof suiteIds.length == 'number') {\r
2662 suiteIds = jasmine.toMap(suiteIds);\r
2663 }\r
2664 if (typeof specIds.length == 'number') {\r
2665 specIds = jasmine.toMap(specIds);\r
2666 }\r
2667\r
2668 var specs = jasmine.getByIds(this.specs(), specIds),\r
2669 suites = jasmine.getByIds(this.suites(), suiteIds),\r
2670 blocks = [], \r
2671 i, length, suite;\r
2672 \r
2673 length = specs.length;\r
2674 for (i = 0; i < length; i++) {\r
2675 suite = specs[i].getRootSuite();\r
2676 if (jasmine.array.indexOf(blocks, suite) === -1) {\r
2677 suite.filter(suiteIds, specIds);\r
2678 blocks.push(suite);\r
2679 }\r
2680 }\r
2681 \r
2682 length = suites.length;\r
2683 for (i = 0; i < length; i++) {\r
2684 suite = suites[i].getRootSuite();\r
2685 if (jasmine.array.indexOf(blocks, suite) === -1) {\r
2686 suite.filter(suiteIds, specIds);\r
2687 blocks.push(suite);\r
2688 }\r
2689 }\r
2690\r
2691 if (blocks.length) {\r
2692 this.queue.blocks = blocks;\r
2693 }\r
2694\r
2695 return this;\r
2696};\r
2697/**\r
2698 * Internal representation of a Jasmine specification, or test.\r
2699 *\r
2700 * @constructor\r
2701 * @param {jasmine.Env} env\r
2702 * @param {jasmine.Suite} suite\r
2703 * @param {String} description\r
2704 */\r
2705jasmine.Spec = function(env, suite, description) {\r
2706 if (!env) {\r
2707 throw new Error('jasmine.Env() required');\r
2708 }\r
2709 if (!suite) {\r
2710 throw new Error('jasmine.Suite() required');\r
2711 }\r
2712 var spec = this;\r
2713 spec.id = env.nextSpecId ? env.nextSpecId() : null;\r
2714 spec.env = env;\r
2715 spec.suite = suite;\r
2716 spec.description = description;\r
2717 spec.queue = new jasmine.Queue(env);\r
2718\r
2719 spec.afterCallbacks = [];\r
2720 spec.spies_ = [];\r
2721\r
2722 spec.results_ = new jasmine.NestedResults();\r
2723 spec.results_.description = description;\r
2724 spec.matchersClass = null;\r
2725};\r
2726\r
2727jasmine.Spec.prototype.getFullName = function() {\r
2728 return this.suite.getFullName() + ' ' + this.description + '.';\r
2729};\r
2730\r
2731\r
2732jasmine.Spec.prototype.results = function() {\r
2733 return this.results_;\r
2734};\r
2735\r
2736/**\r
2737 * All parameters are pretty-printed and concatenated together, then written to the spec's output.\r
2738 *\r
2739 * Be careful not to leave calls to <code>jasmine.log</code> in production code.\r
2740 */\r
2741jasmine.Spec.prototype.log = function() {\r
2742 return this.results_.log(arguments);\r
2743};\r
2744\r
2745jasmine.Spec.prototype.runs = function (func) {\r
2746 var block = new jasmine.Block(this.env, func, this);\r
2747 this.addToQueue(block);\r
2748 return this;\r
2749};\r
2750\r
2751jasmine.Spec.prototype.addToQueue = function (block) {\r
2752 if (this.queue.isRunning()) {\r
2753 this.queue.insertNext(block);\r
2754 } else {\r
2755 this.queue.add(block);\r
2756 }\r
2757};\r
2758\r
2759/**\r
2760 * @param {jasmine.ExpectationResult} result\r
2761 */\r
2762jasmine.Spec.prototype.addMatcherResult = function(result) {\r
2763 this.results_.addResult(result);\r
2764};\r
2765\r
2766jasmine.Spec.prototype.expect = function(actual) {\r
2767 var positive = new (this.getMatchersClass_())(this.env, actual, this);\r
2768 positive.not = new (this.getMatchersClass_())(this.env, actual, this, true);\r
2769 return positive;\r
2770};\r
2771\r
2772/**\r
2773 * Waits a fixed time period before moving to the next block.\r
2774 *\r
2775 * @deprecated Use waitsFor() instead\r
2776 * @param {Number} timeout milliseconds to wait\r
2777 */\r
2778jasmine.Spec.prototype.waits = function(timeout) {\r
2779 var waitsFunc = new jasmine.WaitsBlock(this.env, timeout, this);\r
2780 this.addToQueue(waitsFunc);\r
2781 return this;\r
2782};\r
2783\r
2784/**\r
2785 * Waits for the latchFunction to return true before proceeding to the next block.\r
2786 *\r
2787 * @param {Function} latchFunction\r
2788 * @param {String} optional_timeoutMessage\r
2789 * @param {Number} optional_timeout\r
2790 */\r
2791jasmine.Spec.prototype.waitsFor = function(latchFunction, optional_timeoutMessage, optional_timeout) {\r
2792 var latchFunction_ = null;\r
2793 var optional_timeoutMessage_ = null;\r
2794 var optional_timeout_ = null;\r
2795\r
2796 for (var i = 0; i < arguments.length; i++) {\r
2797 var arg = arguments[i];\r
2798 switch (typeof arg) {\r
2799 case 'function':\r
2800 latchFunction_ = arg;\r
2801 break;\r
2802 case 'string':\r
2803 optional_timeoutMessage_ = arg;\r
2804 break;\r
2805 case 'number':\r
2806 optional_timeout_ = arg;\r
2807 break;\r
2808 }\r
2809 }\r
2810\r
2811 var waitsForFunc = new jasmine.WaitsForBlock(this.env, optional_timeout_, latchFunction_, optional_timeoutMessage_, this);\r
2812 this.addToQueue(waitsForFunc);\r
2813 return this;\r
2814};\r
2815\r
2816jasmine.Spec.prototype.fail = function (e) {\r
2817 var expectationResult = new jasmine.ExpectationResult({\r
2818 passed: false,\r
2819 message: e ? jasmine.util.formatException(e) : 'Exception',\r
2820 trace: { stack: e.stack }\r
2821 });\r
2822 this.results_.addResult(expectationResult);\r
2823};\r
2824\r
2825jasmine.Spec.prototype.getMatchersClass_ = function() {\r
2826 return this.matchersClass || this.env.matchersClass;\r
2827};\r
2828\r
2829jasmine.Spec.prototype.addMatchers = function(matchersPrototype) {\r
2830 var parent = this.getMatchersClass_();\r
2831 var newMatchersClass = function() {\r
2832 parent.apply(this, arguments);\r
2833 };\r
2834 jasmine.util.inherit(newMatchersClass, parent);\r
2835 jasmine.Matchers.wrapInto_(matchersPrototype, newMatchersClass);\r
2836 this.matchersClass = newMatchersClass;\r
2837};\r
2838\r
2839jasmine.Spec.prototype.finishCallback = function() {\r
2840 this.env.reporter.reportSpecResults(this);\r
2841};\r
2842\r
2843jasmine.Spec.prototype.finish = function(onComplete) {\r
2844 this.removeAllSpies();\r
2845 this.finishCallback();\r
2846 if (onComplete) {\r
2847 onComplete();\r
2848 }\r
2849};\r
2850\r
2851jasmine.Spec.prototype.after = function(doAfter) {\r
2852 if (this.queue.isRunning()) {\r
2853 this.queue.add(new jasmine.Block(this.env, doAfter, this), true);\r
2854 } else {\r
2855 this.afterCallbacks.unshift(doAfter);\r
2856 }\r
2857};\r
2858\r
2859jasmine.Spec.prototype.execute = function(onComplete) {\r
2860 var spec = this;\r
2861 if (!spec.env.specFilter(spec)) {\r
2862 spec.results_.skipped = true;\r
2863 spec.finish(onComplete);\r
2864 return;\r
2865 }\r
2866\r
2867 this.env.reporter.reportSpecStarting(this);\r
2868\r
2869 spec.env.currentSpec = spec;\r
2870\r
2871 spec.addBeforesAndAftersToQueue();\r
2872\r
2873 spec.queue.start(function () {\r
2874 spec.finish(onComplete);\r
2875 });\r
2876};\r
2877\r
2878jasmine.Spec.prototype.addBeforesAndAftersToQueue = function() {\r
2879 var runner = this.env.currentRunner();\r
2880 var i;\r
2881\r
2882 for (var suite = this.suite; suite; suite = suite.parentSuite) {\r
2883 for (i = 0; i < suite.before_.length; i++) {\r
2884 this.queue.addBefore(new jasmine.Block(this.env, suite.before_[i], this));\r
2885 }\r
2886 }\r
2887 for (i = 0; i < runner.before_.length; i++) {\r
2888 this.queue.addBefore(new jasmine.Block(this.env, runner.before_[i], this));\r
2889 }\r
2890 for (i = 0; i < this.afterCallbacks.length; i++) {\r
2891 this.queue.add(new jasmine.Block(this.env, this.afterCallbacks[i], this), true);\r
2892 }\r
2893 for (suite = this.suite; suite; suite = suite.parentSuite) {\r
2894 for (i = 0; i < suite.after_.length; i++) {\r
2895 this.queue.add(new jasmine.Block(this.env, suite.after_[i], this), true);\r
2896 }\r
2897 }\r
2898 for (i = 0; i < runner.after_.length; i++) {\r
2899 this.queue.add(new jasmine.Block(this.env, runner.after_[i], this), true);\r
2900 }\r
2901};\r
2902\r
2903jasmine.Spec.prototype.explodes = function() {\r
2904 throw 'explodes function should not have been called';\r
2905};\r
2906\r
2907jasmine.Spec.prototype.spyOn = function(obj, methodName, ignoreMethodDoesntExist) {\r
2908 if (obj == jasmine.undefined) {\r
2909 throw "spyOn could not find an object to spy upon for " + methodName + "()";\r
2910 }\r
2911\r
2912 if (!ignoreMethodDoesntExist && obj[methodName] === jasmine.undefined) {\r
2913 throw methodName + '() method does not exist';\r
2914 }\r
2915\r
2916 if (!ignoreMethodDoesntExist && obj[methodName] && obj[methodName].isSpy) {\r
2917 throw new Error(methodName + ' has already been spied upon');\r
2918 }\r
2919\r
2920 var spyObj = jasmine.createSpy(methodName);\r
2921\r
2922 this.spies_.push(spyObj);\r
2923 spyObj.baseObj = obj;\r
2924 spyObj.methodName = methodName;\r
2925 spyObj.originalValue = obj[methodName];\r
2926\r
2927 obj[methodName] = spyObj;\r
2928\r
2929 return spyObj;\r
2930};\r
2931\r
2932jasmine.Spec.prototype.removeAllSpies = function() {\r
2933 for (var i = 0; i < this.spies_.length; i++) {\r
2934 var spy = this.spies_[i];\r
2935 spy.baseObj[spy.methodName] = spy.originalValue;\r
2936 }\r
2937 this.spies_ = [];\r
2938};\r
2939\r
2940(function () {\r
2941 var _Spec = jasmine.Spec,\r
2942 proto = _Spec.prototype;\r
2943\r
2944 jasmine.Spec = function () {\r
2945 _Spec.apply(this, arguments);\r
2946 this.fileName = jasmine.getCurrentScript();\r
2947 this.id = jasmine.hashString(this.getFullName(), this.suite.id);\r
2948 };\r
2949\r
2950 jasmine.Spec.prototype = proto;\r
2951\r
2952 // Override: adds the error to the result\r
2953 proto.fail = function (e) {\r
2954 var expectationResult = new jasmine.ExpectationResult({\r
2955 passed: false,\r
2956 message: e ? jasmine.util.formatException(e) : 'Exception'\r
2957 });\r
2958 // Modification start\r
2959 if (e instanceof Error) {\r
2960 expectationResult.error = e;\r
2961 }\r
2962 // Modification end\r
2963 this.results_.addResult(expectationResult);\r
2964 };\r
2965\r
2966 proto.execute = function(onComplete) {\r
2967 var spec = this;\r
2968 if (!spec.env.specFilter(spec)) {\r
2969 spec.results_.skipped = true;\r
2970 onComplete();\r
2971 return;\r
2972 }\r
2973\r
2974 this.env.reporter.reportSpecStarting(this);\r
2975\r
2976 if (spec.isDisabled()) {\r
2977 spec.results_.skipped = true;\r
2978 spec.finish(onComplete);\r
2979 return;\r
2980 }\r
2981\r
2982 spec.env.currentSpec = spec;\r
2983\r
2984 spec.addBeforesAndAftersToQueue();\r
2985\r
2986 if (spec.debugBlocks && jasmine.getOptions().debug === true) {\r
2987 var blockIdx = jasmine.getOptions().block;\r
2988 if (typeof blockIdx !== 'undefined') {\r
2989 blockIdx = parseInt(blockIdx);\r
2990 var blocks = this.queue.blocks,\r
2991 length = blocks.length,\r
2992 i = 0,\r
2993 block;\r
2994\r
2995 for (; i < length; i++) {\r
2996 block = blocks[i];\r
2997 if (i === blockIdx) {\r
2998 block.func = jasmine.generateDebuggableBlock(block.func);\r
2999 }\r
3000 }\r
3001 }\r
3002 jasmine.showDebugPrompt(function() {\r
3003 spec.queue.start(function () {\r
3004 spec.finish(onComplete);\r
3005 });\r
3006 });\r
3007 } else {\r
3008 spec.queue.start(function () {\r
3009 spec.finish(onComplete);\r
3010 });\r
3011 }\r
3012 };\r
3013\r
3014 proto.enabled = true;\r
3015\r
3016 proto.isEnabled = function() {\r
3017 return this.enabled;\r
3018 };\r
3019\r
3020 proto.isDisabled = function() {\r
3021 return !this.enabled;\r
3022 };\r
3023\r
3024 proto.disable = function() {\r
3025 this.enabled = false;\r
3026\r
3027 return this;\r
3028 };\r
3029\r
3030 proto.enable = function() {\r
3031 this.enabled = true;\r
3032\r
3033 return this;\r
3034 };\r
3035\r
3036 proto.getRootSuite = function() {\r
3037 var suite = this.suite;\r
3038\r
3039 while (suite.parentSuite) {\r
3040 suite = suite.parentSuite;\r
3041 }\r
3042\r
3043 return suite;\r
3044 };\r
3045})();\r
3046\r
3047/**\r
3048 * Works just like waits() and waitsFor(), except waits for the next animationFrame\r
3049 */\r
3050function waitsForAnimation() {\r
3051 runs(function() {\r
3052 var done = false;\r
3053 Ext.Function.requestAnimationFrame(function() {\r
3054 done = true;\r
3055 });\r
3056 waitsFor(function() {\r
3057 return done;\r
3058 });\r
3059 });\r
3060}\r
3061/**\r
3062 * Internal representation of a Jasmine suite.\r
3063 *\r
3064 * @constructor\r
3065 * @param {jasmine.Env} env\r
3066 * @param {String} description\r
3067 * @param {Function} specDefinitions\r
3068 * @param {jasmine.Suite} parentSuite\r
3069 */\r
3070jasmine.Suite = function(env, description, specDefinitions, parentSuite) {\r
3071 var self = this;\r
3072 self.id = env.nextSuiteId ? env.nextSuiteId() : null;\r
3073 self.description = description;\r
3074 self.queue = new jasmine.Queue(env);\r
3075 self.parentSuite = parentSuite;\r
3076 self.env = env;\r
3077 self.before_ = [];\r
3078 self.after_ = [];\r
3079 self.children_ = [];\r
3080 self.suites_ = [];\r
3081 self.specs_ = [];\r
3082};\r
3083\r
3084jasmine.Suite.prototype.getFullName = function() {\r
3085 var fullName = this.description;\r
3086 for (var parentSuite = this.parentSuite; parentSuite; parentSuite = parentSuite.parentSuite) {\r
3087 fullName = parentSuite.description + ' ' + fullName;\r
3088 }\r
3089 return fullName;\r
3090};\r
3091\r
3092jasmine.Suite.prototype.finish = function(onComplete) {\r
3093 this.env.reporter.reportSuiteResults(this);\r
3094 this.finished = true;\r
3095 if (typeof(onComplete) == 'function') {\r
3096 onComplete();\r
3097 }\r
3098};\r
3099\r
3100jasmine.Suite.prototype.beforeEach = function(beforeEachFunction) {\r
3101 beforeEachFunction.typeName = 'beforeEach';\r
3102 this.before_.unshift(beforeEachFunction);\r
3103};\r
3104\r
3105jasmine.Suite.prototype.afterEach = function(afterEachFunction) {\r
3106 afterEachFunction.typeName = 'afterEach';\r
3107 this.after_.unshift(afterEachFunction);\r
3108};\r
3109\r
3110jasmine.Suite.prototype.results = function() {\r
3111 return this.queue.results();\r
3112};\r
3113\r
3114jasmine.Suite.prototype.add = function(suiteOrSpec) {\r
3115 this.children_.push(suiteOrSpec);\r
3116 if (suiteOrSpec instanceof jasmine.Suite) {\r
3117 this.suites_.push(suiteOrSpec);\r
3118 this.env.currentRunner().addSuite(suiteOrSpec);\r
3119 } else {\r
3120 this.specs_.push(suiteOrSpec);\r
3121 }\r
3122 this.queue.add(suiteOrSpec);\r
3123};\r
3124\r
3125jasmine.Suite.prototype.specs = function() {\r
3126 return this.specs_;\r
3127};\r
3128\r
3129jasmine.Suite.prototype.suites = function() {\r
3130 return this.suites_;\r
3131};\r
3132\r
3133jasmine.Suite.prototype.children = function() {\r
3134 return this.children_;\r
3135};\r
3136\r
3137jasmine.Suite.prototype.execute = function(onComplete) {\r
3138 var self = this;\r
3139 this.queue.start(function () {\r
3140 self.finish(onComplete);\r
3141 });\r
3142};\r
3143(function () {\r
3144 var _Suite = jasmine.Suite,\r
3145 proto = _Suite.prototype;\r
3146\r
3147 jasmine.Suite = function () {\r
3148 _Suite.apply(this, arguments);\r
3149\r
3150 var parentSuite = this.parentSuite;\r
3151 this.fileName = jasmine.getCurrentScript();\r
3152 this.id = jasmine.hashString(this.getFullName(), parentSuite ? parentSuite.id : 0);\r
3153 };\r
3154 \r
3155 jasmine.Suite.prototype = proto;\r
3156 \r
3157 proto.execute = function(onComplete) {\r
3158 var self = this;\r
3159 self.env.reporter.reportSuiteStarting(self); // override\r
3160\r
3161 if (self.isDisabled()) {\r
3162 self.results = self.forceSkippedResults;\r
3163 self.disableChildren();\r
3164 }\r
3165\r
3166 this.queue.start(function () {\r
3167 self.finish(onComplete);\r
3168 });\r
3169 };\r
3170\r
3171 proto.enabled = true;\r
3172\r
3173 proto.isEnabled = function() {\r
3174 return this.enabled;\r
3175 };\r
3176\r
3177 proto.isDisabled = function() {\r
3178 return !this.enabled;\r
3179 };\r
3180\r
3181 proto.disable = function() {\r
3182 this.enabled = false;\r
3183 return this;\r
3184 };\r
3185\r
3186 proto.enable = function() {\r
3187 this.enabled = true;\r
3188 return this;\r
3189 };\r
3190\r
3191 proto.forceSkippedResults = function() {\r
3192 var results = this.queue.results();\r
3193 results.skipped = true;\r
3194 return results;\r
3195 };\r
3196\r
3197 proto.disableChildren = function() {\r
3198 var children = this.children(),\r
3199 length = children.length,\r
3200 i = 0;\r
3201\r
3202 for (; i < length; i++) {\r
3203 children[i].disable();\r
3204 }\r
3205\r
3206 return this;\r
3207 };\r
3208\r
3209 proto.filter = function (suiteIds, specIds) {\r
3210\r
3211 if (!suiteIds[this.id]) {\r
3212 var specs = this.specs(),\r
3213 suites = this.suites(),\r
3214 spec, i, suite, length;\r
3215\r
3216 length = specs.length;\r
3217\r
3218 for (i = 0; i < length; i++) {\r
3219 spec = specs[i];\r
3220 if (!specIds[spec.id]) {\r
3221 jasmine.array.remove(this.queue.blocks, spec);\r
3222 }\r
3223 }\r
3224\r
3225 length = suites.length;\r
3226\r
3227 for (i = 0; i < length; i++) {\r
3228 suite = suites[i];\r
3229 suite.filter(suiteIds, specIds);\r
3230 if (suite.empty) {\r
3231 jasmine.array.remove(this.queue.blocks, suite);\r
3232 }\r
3233 }\r
3234\r
3235 if (this.queue.blocks.length === 0) {\r
3236 this.empty = true;\r
3237 }\r
3238 }\r
3239\r
3240 return this;\r
3241 };\r
3242\r
3243 proto.getRootSuite = function() {\r
3244 var suite = this;\r
3245\r
3246 while (suite.parentSuite) {\r
3247 suite = suite.parentSuite;\r
3248 }\r
3249\r
3250 return suite;\r
3251 };\r
3252\r
3253})();\r
3254jasmine.WaitsBlock = function(env, timeout, spec) {\r
3255 this.timeout = timeout;\r
3256 jasmine.Block.call(this, env, null, spec);\r
3257};\r
3258\r
3259jasmine.util.inherit(jasmine.WaitsBlock, jasmine.Block);\r
3260\r
3261jasmine.WaitsBlock.prototype.execute = function (onComplete) {\r
3262 if (jasmine.VERBOSE) {\r
3263 this.env.reporter.log('>> Jasmine waiting for ' + this.timeout + ' ms...');\r
3264 }\r
3265 this.env.setTimeout(function () {\r
3266 onComplete();\r
3267 }, this.timeout);\r
3268};\r
3269/**\r
3270 * A block which waits for some condition to become true, with timeout.\r
3271 *\r
3272 * @constructor\r
3273 * @extends jasmine.Block\r
3274 * @param {jasmine.Env} env The Jasmine environment.\r
3275 * @param {Number} timeout The maximum time in milliseconds to wait for the condition to become true.\r
3276 * @param {Function} latchFunction A function which returns true when the desired condition has been met.\r
3277 * @param {String} message The message to display if the desired condition hasn't been met within the given time period.\r
3278 * @param {jasmine.Spec} spec The Jasmine spec.\r
3279 */\r
3280jasmine.WaitsForBlock = function(env, timeout, latchFunction, message, spec) {\r
3281 this.timeout = timeout || env.defaultTimeoutInterval;\r
3282 this.latchFunction = latchFunction;\r
3283 this.message = message;\r
3284 this.totalTimeSpentWaitingForLatch = 0;\r
3285 jasmine.Block.call(this, env, null, spec);\r
3286};\r
3287jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);\r
3288\r
3289jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;\r
3290\r
3291jasmine.WaitsForBlock.prototype.execute = function(onComplete) {\r
3292 if (jasmine.VERBOSE) {\r
3293 this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));\r
3294 }\r
3295 var latchFunctionResult;\r
3296 try {\r
3297 latchFunctionResult = this.latchFunction.apply(this.spec);\r
3298 } catch (e) {\r
3299 this.spec.fail(e);\r
3300 onComplete();\r
3301 return;\r
3302 }\r
3303\r
3304 if (latchFunctionResult) {\r
3305 onComplete();\r
3306 } else if (this.totalTimeSpentWaitingForLatch >= this.timeout) {\r
3307 var message = 'timed out after ' + this.timeout + ' msec waiting for ' + (this.message || 'something to happen');\r
3308 this.spec.fail({\r
3309 name: 'timeout',\r
3310 message: message\r
3311 });\r
3312\r
3313 this.abort = true;\r
3314 onComplete();\r
3315 } else {\r
3316 this.totalTimeSpentWaitingForLatch += jasmine.WaitsForBlock.TIMEOUT_INCREMENT;\r
3317 var self = this;\r
3318 this.env.setTimeout(function() {\r
3319 self.execute(onComplete);\r
3320 }, jasmine.WaitsForBlock.TIMEOUT_INCREMENT);\r
3321 }\r
3322};\r
3323// Mock setTimeout, clearTimeout\r
3324// Contributed by Pivotal Computer Systems, www.pivotalsf.com\r
3325\r
3326jasmine.FakeTimer = function() {\r
3327 this.reset();\r
3328\r
3329 var self = this;\r
3330 self.setTimeout = function(funcToCall, millis) {\r
3331 self.timeoutsMade++;\r
3332 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, false);\r
3333 return self.timeoutsMade;\r
3334 };\r
3335\r
3336 self.setInterval = function(funcToCall, millis) {\r
3337 self.timeoutsMade++;\r
3338 self.scheduleFunction(self.timeoutsMade, funcToCall, millis, true);\r
3339 return self.timeoutsMade;\r
3340 };\r
3341\r
3342 self.clearTimeout = function(timeoutKey) {\r
3343 self.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3344 };\r
3345\r
3346 self.clearInterval = function(timeoutKey) {\r
3347 self.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3348 };\r
3349\r
3350};\r
3351\r
3352jasmine.FakeTimer.prototype.reset = function() {\r
3353 this.timeoutsMade = 0;\r
3354 this.scheduledFunctions = {};\r
3355 this.nowMillis = 0;\r
3356};\r
3357\r
3358jasmine.FakeTimer.prototype.tick = function(millis) {\r
3359 var oldMillis = this.nowMillis;\r
3360 var newMillis = oldMillis + millis;\r
3361 this.runFunctionsWithinRange(oldMillis, newMillis);\r
3362 this.nowMillis = newMillis;\r
3363};\r
3364\r
3365jasmine.FakeTimer.prototype.runFunctionsWithinRange = function(oldMillis, nowMillis) {\r
3366 var scheduledFunc;\r
3367 var funcsToRun = [];\r
3368 for (var timeoutKey in this.scheduledFunctions) {\r
3369 scheduledFunc = this.scheduledFunctions[timeoutKey];\r
3370 if (scheduledFunc != jasmine.undefined &&\r
3371 scheduledFunc.runAtMillis >= oldMillis &&\r
3372 scheduledFunc.runAtMillis <= nowMillis) {\r
3373 funcsToRun.push(scheduledFunc);\r
3374 this.scheduledFunctions[timeoutKey] = jasmine.undefined;\r
3375 }\r
3376 }\r
3377\r
3378 if (funcsToRun.length > 0) {\r
3379 funcsToRun.sort(function(a, b) {\r
3380 return a.runAtMillis - b.runAtMillis;\r
3381 });\r
3382 for (var i = 0; i < funcsToRun.length; ++i) {\r
3383 try {\r
3384 var funcToRun = funcsToRun[i];\r
3385 this.nowMillis = funcToRun.runAtMillis;\r
3386 funcToRun.funcToCall();\r
3387 if (funcToRun.recurring) {\r
3388 this.scheduleFunction(funcToRun.timeoutKey,\r
3389 funcToRun.funcToCall,\r
3390 funcToRun.millis,\r
3391 true);\r
3392 }\r
3393 } catch(e) {\r
3394 }\r
3395 }\r
3396 this.runFunctionsWithinRange(oldMillis, nowMillis);\r
3397 }\r
3398};\r
3399\r
3400jasmine.FakeTimer.prototype.scheduleFunction = function(timeoutKey, funcToCall, millis, recurring) {\r
3401 this.scheduledFunctions[timeoutKey] = {\r
3402 runAtMillis: this.nowMillis + millis,\r
3403 funcToCall: funcToCall,\r
3404 recurring: recurring,\r
3405 timeoutKey: timeoutKey,\r
3406 millis: millis\r
3407 };\r
3408};\r
3409\r
3410/**\r
3411 * @namespace\r
3412 */\r
3413jasmine.Clock = {\r
3414 defaultFakeTimer: new jasmine.FakeTimer(),\r
3415\r
3416 reset: function() {\r
3417 jasmine.Clock.assertInstalled();\r
3418 jasmine.Clock.defaultFakeTimer.reset();\r
3419 },\r
3420\r
3421 tick: function(millis) {\r
3422 jasmine.Clock.assertInstalled();\r
3423 jasmine.Clock.defaultFakeTimer.tick(millis);\r
3424 },\r
3425\r
3426 runFunctionsWithinRange: function(oldMillis, nowMillis) {\r
3427 jasmine.Clock.defaultFakeTimer.runFunctionsWithinRange(oldMillis, nowMillis);\r
3428 },\r
3429\r
3430 scheduleFunction: function(timeoutKey, funcToCall, millis, recurring) {\r
3431 jasmine.Clock.defaultFakeTimer.scheduleFunction(timeoutKey, funcToCall, millis, recurring);\r
3432 },\r
3433\r
3434 useMock: function() {\r
3435 if (!jasmine.Clock.isInstalled()) {\r
3436 var spec = jasmine.getEnv().currentSpec;\r
3437 spec.after(jasmine.Clock.uninstallMock);\r
3438\r
3439 jasmine.Clock.installMock();\r
3440 }\r
3441 },\r
3442\r
3443 installMock: function() {\r
3444 jasmine.Clock.installed = jasmine.Clock.defaultFakeTimer;\r
3445 },\r
3446\r
3447 uninstallMock: function() {\r
3448 jasmine.Clock.assertInstalled();\r
3449 jasmine.Clock.installed = jasmine.Clock.real;\r
3450 },\r
3451\r
3452 real: {\r
3453 setTimeout: jasmine.getGlobal().setTimeout,\r
3454 clearTimeout: jasmine.getGlobal().clearTimeout,\r
3455 setInterval: jasmine.getGlobal().setInterval,\r
3456 clearInterval: jasmine.getGlobal().clearInterval\r
3457 },\r
3458\r
3459 assertInstalled: function() {\r
3460 if (!jasmine.Clock.isInstalled()) {\r
3461 throw new Error("Mock clock is not installed, use jasmine.Clock.useMock()");\r
3462 }\r
3463 },\r
3464\r
3465 isInstalled: function() {\r
3466 return jasmine.Clock.installed == jasmine.Clock.defaultFakeTimer;\r
3467 },\r
3468\r
3469 installed: null\r
3470};\r
3471jasmine.Clock.installed = jasmine.Clock.real;\r
3472\r
3473//else for IE support\r
3474jasmine.getGlobal().setTimeout = function(funcToCall, millis) {\r
3475 if (jasmine.Clock.installed.setTimeout.apply) {\r
3476 return jasmine.Clock.installed.setTimeout.apply(this, arguments);\r
3477 } else {\r
3478 return jasmine.Clock.installed.setTimeout(funcToCall, millis);\r
3479 }\r
3480};\r
3481\r
3482jasmine.getGlobal().setInterval = function(funcToCall, millis) {\r
3483 if (jasmine.Clock.installed.setInterval.apply) {\r
3484 return jasmine.Clock.installed.setInterval.apply(this, arguments);\r
3485 } else {\r
3486 return jasmine.Clock.installed.setInterval(funcToCall, millis);\r
3487 }\r
3488};\r
3489\r
3490jasmine.getGlobal().clearTimeout = function(timeoutKey) {\r
3491 if (jasmine.Clock.installed.clearTimeout.apply) {\r
3492 return jasmine.Clock.installed.clearTimeout.apply(this, arguments);\r
3493 } else {\r
3494 return jasmine.Clock.installed.clearTimeout(timeoutKey);\r
3495 }\r
3496};\r
3497\r
3498jasmine.getGlobal().clearInterval = function(timeoutKey) {\r
3499 if (jasmine.Clock.installed.clearTimeout.apply) {\r
3500 return jasmine.Clock.installed.clearInterval.apply(this, arguments);\r
3501 } else {\r
3502 return jasmine.Clock.installed.clearInterval(timeoutKey);\r
3503 }\r
3504};\r
3505\r
3506/**\r
3507 * Utility function to fire a fake mouse event to a given target element\r
3508 */\r
3509jasmine.fireMouseEvent = function (target, type, x, y, button) {\r
3510 var e,\r
3511 doc,\r
3512 docEl,\r
3513 body,\r
3514 ret;\r
3515 \r
3516 target = Ext.getDom(target);\r
3517 doc = target.ownerDocument || document;\r
3518 x = x || 0;\r
3519 y = y || 0;\r
3520 \r
3521 if (doc.createEventObject){ // IE event model\r
3522 e = doc.createEventObject();\r
3523 docEl = doc.documentElement;\r
3524 body = doc.body;\r
3525 x = x + (docEl && docEl.clientLeft || 0) + (body && body.clientLeft || 0);\r
3526 y = y + (docEl && docEl.clientTop || 0) + (body && body.clientLeft || 0);\r
3527 Ext.apply(e, {\r
3528 bubbles: true,\r
3529 cancelable: true,\r
3530 screenX: x,\r
3531 screenY: y,\r
3532 clientX: x,\r
3533 clientY: y,\r
3534 button: button || 1\r
3535 });\r
3536 ret = target.fireEvent('on' + type, e);\r
3537 } else {\r
3538 e = doc.createEvent("MouseEvents");\r
3539 e.initMouseEvent(type, true, true, doc.defaultView || doc.parentWindow, 1, x, y, x, y, false, false, false, false, button || 0, null);\r
3540 ret = target.dispatchEvent(e);\r
3541 }\r
3542 \r
3543 return (ret === false ? ret : e);\r
3544};\r
3545\r
3546/**\r
3547 * Fires a pointer event. Since PointerEvents cannot yet be directly constructed,\r
3548 * we fake it by constructing a mouse event and setting its pointer id. This method\r
3549 * should typically be used when (Ext.supports.PointerEvents || Ext.supports.MSPointerEvents).\r
3550 * @param {String/Ext.Element/HTMLElement} target\r
3551 * @param {String} type The name of the event to fire\r
3552 * @param {Number} pointerId A unique id for the pointer, for more on pointerId see\r
3553 * http://www.w3.org/TR/pointerevents/\r
3554 * @param {Number} x The x coordinate\r
3555 * @param {Number} y The y coordinate\r
3556 * @param {Number} button\r
3557 * @return {Boolean} true if the event was successfully dispatched\r
3558 */\r
3559jasmine.firePointerEvent = function(target, type, pointerId, x, y, button) {\r
3560 var doc = document,\r
3561 e = doc.createEvent("MouseEvents"),\r
3562 target = Ext.getDom(target),\r
3563 dispatched;\r
3564\r
3565 e.initMouseEvent(\r
3566 type, // type\r
3567 true, // canBubble\r
3568 true, // cancelable\r
3569 doc.defaultView || doc.parentWindow, // view\r
3570 1, // detail\r
3571 x, // screenX\r
3572 y, // screenY\r
3573 x, // clientX\r
3574 y, // clientY\r
3575 false, // ctrlKey\r
3576 false, // altKey\r
3577 false, // shiftKey\r
3578 false, // metaKey\r
3579 button || 0, // button\r
3580 null // relatedTarget\r
3581 );\r
3582 e.pointerId = pointerId || 1;\r
3583 target.setPointerCapture = Ext.emptyFn;\r
3584\r
3585 dispatched = target.dispatchEvent(e);\r
3586\r
3587 delete target.setPointerCapture;\r
3588\r
3589 return dispatched;\r
3590};\r
3591\r
3592jasmine.createTouchList = function(touchList, target) {\r
3593 var doc = document,\r
3594 i = 0,\r
3595 ln = touchList.length,\r
3596 touches = [],\r
3597 touchCfg;\r
3598\r
3599 for (; i < ln; i++) {\r
3600 touchCfg = touchList[i];\r
3601 touches.push(doc.createTouch(\r
3602 doc.defaultView || doc.parentWindow,\r
3603 touchCfg.target || target,\r
3604 // use 1 as the default ID, so that tests that are only concerned with a single\r
3605 // touch event don't need to worry about providing an ID\r
3606 touchCfg.identifier || 1,\r
3607 touchCfg.pageX,\r
3608 touchCfg.pageY,\r
3609 touchCfg.screenX || touchCfg.pageX, // use pageX/Y as the default for screenXY\r
3610 touchCfg.screenY || touchCfg.pageY\r
3611 ));\r
3612 }\r
3613\r
3614 return doc.createTouchList.apply(doc, touches);\r
3615};\r
3616\r
3617/**\r
3618 * Utility for emulating a touch event. This method should typically only be used when\r
3619 * Ext.supports.TouchEvents. Recommended reading for understanding how touch events work:\r
3620 * http://www.w3.org/TR/touch-events/\r
3621 * @param {String/Ext.Element/HTMLElement} target\r
3622 * @param {String} type The name of the event to fire\r
3623 * @param {Object[]} touches An array of config objects for constructing the event object's\r
3624 * "touches". The config objects conform to the following interface:\r
3625 * http://www.w3.org/TR/touch-events/#idl-def-Touch The only required properties\r
3626 * are pageX and pageY. this method provides defaults for the others.\r
3627 * @param {Object[]} changedTouches An array of config objects for constructing the event\r
3628 * object's "changedTouches" (defaults to the same value as the `touches` param)\r
3629 * @param {Object[]} targetTouches An array of config objects for constructing the event\r
3630 * object's "targetTouches" (defaults to the same value as the `touches` param)\r
3631 * @param {Number} scale\r
3632 * @param {Number} rotation\r
3633 * @return {Boolean} true if the event was successfully dispatched\r
3634 */\r
3635jasmine.fireTouchEvent = function(target, type, touches, changedTouches, targetTouches) {\r
3636 var doc = document,\r
3637 // Couldn't figure out how to set touches, changedTouches targetTouches on a "real"\r
3638 // TouchEvent, initTouchEvent seems to ignore the parameters documented here:\r
3639 // https://developer.apple.com/library/safari/documentation/UserExperience/Reference/TouchEventClassReference/TouchEvent/TouchEvent.htmlTouchLists\r
3640 // Apparently directly assigning to e.touches after creating a TouchEvent doesn't\r
3641 // work either so the best we can do is just make a CustomEvent and fake it.\r
3642 e = new CustomEvent(type, {\r
3643 bubbles: true,\r
3644 cancelable: true\r
3645 }),\r
3646 target = Ext.getDom(target);\r
3647\r
3648 Ext.apply(e, {\r
3649 target: target,\r
3650 touches: jasmine.createTouchList(touches, target),\r
3651 changedTouches: jasmine.createTouchList(changedTouches ? changedTouches : touches, target),\r
3652 targetTouches: jasmine.createTouchList(targetTouches ? targetTouches : touches, target)\r
3653 });\r
3654\r
3655 return target.dispatchEvent(e);\r
3656};\r
3657\r
3658/**\r
3659 * Utility function to fire a fake key event to a given target element\r
3660 */\r
3661jasmine.fireKeyEvent = function(target, type, key) {\r
3662 var e,\r
3663 doc;\r
3664 target = Ext.getDom(target);\r
3665 doc = target.ownerDocument || document;\r
3666 if (doc.createEventObject) { //IE event model\r
3667 e = doc.createEventObject();\r
3668 Ext.apply(e, {\r
3669 bubbles: true,\r
3670 cancelable: true,\r
3671 keyCode: key\r
3672 });\r
3673 return target.fireEvent('on' + type, e);\r
3674 } else {\r
3675 e = doc.createEvent("Events");\r
3676 e.initEvent(type, true, true);\r
3677 e.keyCode = key;\r
3678 return target.dispatchEvent(e);\r
3679 }\r
3680};\r
3681\r
3682/*\r
3683This version is commented out because there is a bug in WebKit that prevents\r
3684key events to be fired with both options and a valid keycode. When the bug gets fixed\r
3685this method can be reintroduced. See https://bugs.webkit.org/show_bug.cgi?id=16735\r
3686jasmine.fireKeyEvent = function(target, type, key, options) {\r
3687 var e, ret, prop;\r
3688 \r
3689 options = options || {};\r
3690 target = Ext.getDom(target);\r
3691 if (document.createEventObject) { //IE event model\r
3692 e = document.createEventObject();\r
3693 Ext.apply(e, {\r
3694 bubbles: true,\r
3695 cancelable: true,\r
3696 keyCode: key\r
3697 });\r
3698 if (options) {\r
3699 for (prop in options) {\r
3700 if (options.hasOwnProperty(prop)) {\r
3701 e[prop] = options[prop];\r
3702 }\r
3703 }\r
3704 }\r
3705 return target.fireEvent('on' + type, e);\r
3706 }\r
3707 else {\r
3708 e = document.createEvent('KeyboardEvent');\r
3709 if (typeof e.initKeyboardEvent != 'undefined') {\r
3710 e.initKeyboardEvent(type, true, true, window,\r
3711 false,\r
3712 false,\r
3713 false,\r
3714 false, 97, 97);\r
3715 } else {\r
3716 e.initKeyEvent(type, true, true, window,\r
3717 options.ctrlKey || false,\r
3718 options.altKey || false,\r
3719 options.shiftKey || false,\r
3720 options.metaKey || false, key, key);\r
3721 }\r
3722 return target.dispatchEvent(e);\r
3723 }\r
3724};\r
3725*/\r
3726var fakeScope = {\r
3727 id: "fakeScope",\r
3728 fakeScope: true\r
3729};\r
3730/**\r
3731 * Class to act as a bridge between the MockAjax class and Ext.data.Connection\r
3732 */\r
3733var MockAjaxManager = {\r
3734 \r
3735 getXhrInstance: null,\r
3736 \r
3737 /**\r
3738 * Pushes methods onto the Connection prototype to make it easier to deal with\r
3739 */\r
3740 addMethods: function(){\r
3741 var Connection = Ext.data.Connection,\r
3742 proto = Connection.prototype;\r
3743 \r
3744 Connection.requestId = 0;\r
3745 MockAjaxManager.getXhrInstance = proto.getXhrInstance;\r
3746 \r
3747 /**\r
3748 * Template method to create the AJAX request\r
3749 */\r
3750 proto.getXhrInstance = function(){\r
3751 return new MockAjax(); \r
3752 };\r
3753 \r
3754 /**\r
3755 * Method to simulate a request completing\r
3756 * @param {Object} response The response\r
3757 * @param {String} id (optional) The id of the completed request\r
3758 */\r
3759 proto.mockComplete = function(response, id){\r
3760 this.mockGetRequestXHR(id).xhr.complete(response);\r
3761 };\r
3762 \r
3763 /**\r
3764 * Get a particular request\r
3765 * @param {String} id (optional) The id of the request\r
3766 */\r
3767 proto.mockGetRequestXHR = function(id){\r
3768 var request;\r
3769 \r
3770 if (id) {\r
3771 request = this.requests[id];\r
3772 } else {\r
3773 // get the first one\r
3774 request = this.mockGetAllRequests()[0];\r
3775 }\r
3776 return request ? request : null;\r
3777 };\r
3778 \r
3779 /**\r
3780 * Gets all the requests from the Connection\r
3781 */\r
3782 proto.mockGetAllRequests = function(){\r
3783 var requests = this.requests,\r
3784 id,\r
3785 request,\r
3786 out = [];\r
3787 \r
3788 for (id in requests) {\r
3789 if (requests.hasOwnProperty(id)) {\r
3790 out.push(requests[id]);\r
3791 }\r
3792 }\r
3793 return out;\r
3794 };\r
3795\r
3796 this.originalExtAjax = Ext.Ajax;\r
3797 Ext.Ajax = new Connection({autoAbort : false});\r
3798 },\r
3799 \r
3800 /**\r
3801 * Restore any changes made by addMethods\r
3802 */\r
3803 removeMethods: function(){\r
3804 var proto = Ext.data.Connection.prototype;\r
3805 delete proto.mockComplete;\r
3806 delete proto.mockGetRequestXHR;\r
3807 proto.getXhrInstance = MockAjaxManager.getXhrInstance;\r
3808 Ext.Ajax = this.originalExtAjax;\r
3809 }\r
3810};\r
3811\r
3812/**\r
3813 * Simple Mock class to represent an XMLHttpRequest\r
3814 */\r
3815var MockAjax = function(){\r
3816 /**\r
3817 * Contains all request headers\r
3818 */\r
3819 this.headers = {};\r
3820 \r
3821 /**\r
3822 * Contains any options specified during sending\r
3823 */\r
3824 this.ajaxOptions = {};\r
3825 \r
3826 this.readyState = 0;\r
3827 \r
3828 this.status = null;\r
3829 \r
3830 this.responseText = this.responseXML = null;\r
3831};\r
3832\r
3833/**\r
3834 * Contains a default response for any synchronous request.\r
3835 */\r
3836MockAjax.prototype.syncDefaults = {\r
3837 responseText: 'data',\r
3838 status: 200,\r
3839 statusText: '',\r
3840 responseXML: null,\r
3841 responseHeaders: {"Content-type": "application/json" }\r
3842};\r
3843\r
3844MockAjax.prototype.readyChange = function() {\r
3845 if (this.onreadystatechange) {\r
3846 this.onreadystatechange();\r
3847 }\r
3848};\r
3849\r
3850/**\r
3851 * Simulate the XHR open method\r
3852 * @param {Object} method\r
3853 * @param {Object} url\r
3854 * @param {Object} async\r
3855 * @param {Object} username\r
3856 * @param {Object} password\r
3857 */\r
3858MockAjax.prototype.open = function(method, url, async, username, password){\r
3859 var options = this.ajaxOptions;\r
3860 options.method = method;\r
3861 options.url = url;\r
3862 options.async = async;\r
3863 options.username = username;\r
3864 options.password = password;\r
3865 this.readyState = 1;\r
3866 this.readyChange();\r
3867};\r
3868\r
3869/**\r
3870 * Simulate the XHR send method\r
3871 * @param {Object} data\r
3872 */\r
3873MockAjax.prototype.send = function(data){\r
3874 this.ajaxOptions.data = data;\r
3875 this.readyState = 2;\r
3876 // if it's a synchronous request, let's just assume it's already finished\r
3877 if (!this.ajaxOptions.async) {\r
3878 this.complete(this.syncDefaults);\r
3879 } else {\r
3880 this.readyChange();\r
3881 }\r
3882};\r
3883\r
3884/**\r
3885 * Simulate the XHR abort method\r
3886 */\r
3887MockAjax.prototype.abort = function(){\r
3888 this.readyState = 0;\r
3889 this.readyChange();\r
3890};\r
3891\r
3892/**\r
3893 * Simulate the XHR setRequestHeader method\r
3894 * @param {Object} header\r
3895 * @param {Object} value\r
3896 */\r
3897MockAjax.prototype.setRequestHeader = function(header, value){\r
3898 this.headers[header] = value;\r
3899};\r
3900\r
3901/**\r
3902 * Simulate the XHR getAllResponseHeaders method\r
3903 */\r
3904MockAjax.prototype.getAllResponseHeaders = function(){\r
3905 return '';\r
3906};\r
3907\r
3908/**\r
3909 * Simulate the XHR getResponseHeader method\r
3910 * @param {Object} name\r
3911 */\r
3912MockAjax.prototype.getResponseHeader = function(name){\r
3913 return this.headers[header];\r
3914};\r
3915\r
3916/**\r
3917 * Simulate the XHR onreadystatechange method\r
3918 */\r
3919MockAjax.prototype.onreadystatechange = function(){\r
3920};\r
3921\r
3922/**\r
3923 * Method for triggering a response completion\r
3924 */\r
3925MockAjax.prototype.complete = function(response){\r
3926 this.responseText = response.responseText || '';\r
3927 this.status = response.status;\r
3928 this.statusText = response.statusText;\r
3929 this.responseXML = response.responseXML || this.xmlDOM(response.responseText);\r
3930 this.responseHeaders = response.responseHeaders || {"Content-type": response.contentType || "application/json" };\r
3931 this.readyState = 4;\r
3932 this.readyChange();\r
3933};\r
3934\r
3935/**\r
3936 * Converts string to XML DOM\r
3937 */\r
3938MockAjax.prototype.xmlDOM = function(xml) {\r
3939 // IE DOMParser support\r
3940 if (!window.DOMParser && window.ActiveXObject) {\r
3941 doc = new ActiveXObject('Microsoft.XMLDOM');\r
3942 doc.async = 'false';\r
3943 DOMParser = function() {};\r
3944 DOMParser.prototype.parseFromString = function(xmlString) {\r
3945 var doc = new ActiveXObject('Microsoft.XMLDOM');\r
3946 doc.async = 'false';\r
3947 doc.loadXML(xmlString);\r
3948 return doc;\r
3949 };\r
3950 } \r
3951\r
3952 try {\r
3953 return (new DOMParser()).parseFromString(xml, "text/xml");\r
3954 } catch (e) {\r
3955 return null;\r
3956 }\r
3957};\r
3958SenchaTestRunner = {\r
3959 allowedGlobals: {},\r
3960 \r
3961 specTimeout: 10 * 1000, // 10 secs\r
3962\r
3963 isRunning: function() {\r
3964 if (!this.reporter) {\r
3965 return false;\r
3966 }\r
3967 \r
3968 var isRunning = this.reporter.isRunning,\r
3969 currentSpec = jasmine.getEnv().currentSpec,\r
3970 testResult = currentSpec._testResult;\r
3971 \r
3972 if (!isRunning || !currentSpec) {\r
3973 return isRunning;\r
3974 }\r
3975 \r
3976 if (jasmine.getOptions().debug !== true && testResult) {\r
3977 var duration = (new Date).getTime() - parseInt(testResult.startTime);\r
3978 if (duration > this.specTimeout) {\r
3979 throw new Error ("The spec '" + currentSpec.getFullName() + "' is taking more than " + (this.specTimeout/1000) + " seconds to complete.");\r
3980 }\r
3981 }\r
3982\r
3983 this.currentSpec = currentSpec;\r
3984 \r
3985 return isRunning;\r
3986 \r
3987 },\r
3988\r
3989 /**\r
3990 * initializes the allowed globals object with all the propeties in the current\r
3991 * window. Should be called after frameworks are loaded but before specs are loaded.\r
3992 */\r
3993 initGlobals: function() {\r
3994 var me = this,\r
3995 allowedGlobals = me.allowedGlobals,\r
3996 prop;\r
3997\r
3998 for (prop in window) {\r
3999 allowedGlobals[prop] = true;\r
4000 }\r
4001\r
4002 // Old Firefox needs these\r
4003 allowedGlobals.getInterface =\r
4004 allowedGlobals.loadFirebugConsole =\r
4005 allowedGlobals._createFirebugConsole =\r
4006 allowedGlobals.netscape = \r
4007 allowedGlobals.XPCSafeJSObjectWrapper =\r
4008 allowedGlobals.XPCNativeWrapper =\r
4009 allowedGlobals.Components =\r
4010 allowedGlobals._firebug =\r
4011 // IE10 F12 dev tools adds this property when opened.\r
4012 allowedGlobals.__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE =\r
4013 // we're going to add the addGlobal function to the window object, so specs can call it\r
4014 allowedGlobals.addGlobal = true;\r
4015 allowedGlobals.id = true; // In Ext JS 4 Ext.get(window) adds an id property\r
4016\r
4017 window.addGlobal = function() {\r
4018 me.addGlobal.apply(me, arguments);\r
4019 };\r
4020 },\r
4021\r
4022 /**\r
4023 * Add an allowed global variable.\r
4024 * @param {String/Array} property The variable name, or array of names\r
4025 */\r
4026 addGlobal: function(property) {\r
4027 var len;\r
4028\r
4029 if (property.charAt) { // string\r
4030 this.allowedGlobals[property] = true;\r
4031 } else { // array\r
4032 for (len = property.length; len--;) {\r
4033 this.allowedGlobals[property[len]] = true;\r
4034 }\r
4035 }\r
4036 },\r
4037\r
4038 checkGlobals: function(spec) {\r
4039 var allowedGlobals = this.allowedGlobals,\r
4040 property, value;\r
4041 \r
4042 for (property in window) {\r
4043 value = window[property];\r
4044 if (value !== undefined && !allowedGlobals[property] &&\r
4045 (!value || // make sure we don't try to do a property lookup on a null value\r
4046 // old browsers (IE6 and opera 11) add element IDs as enumerable properties\r
4047 // of the window object, so make sure the global var is not a HTMLElement\r
4048 value.nodeType !== 1 &&\r
4049 // make sure it isn't a reference to a window object. This happens in\r
4050 // some browsers (e.g. IE6) when the document contains iframes. The\r
4051 // frames' window objects are referenced by id in the parent window object.\r
4052 !(value.location && value.document))) {\r
4053 spec.fail('Bad global variable: ' + property + ' = ' + value);\r
4054 allowedGlobals[property] = true;\r
4055 }\r
4056 } \r
4057 },\r
4058 \r
4059 results : [],\r
4060 \r
4061 Reporter : function() { \r
4062 var me = this;\r
4063 // some browsers add an empty text node in the document.body. This ensures\r
4064 // we start with an emtpy body\r
4065 document.body.innerHTML = '';\r
4066 me.suites = {};\r
4067 me.results = {\r
4068 startTime: null,\r
4069 endTime: null,\r
4070 failures: 0,\r
4071 suitesCount: 0,\r
4072 specsCount: 0,\r
4073 suites: []\r
4074 };\r
4075 SenchaTestRunner.reporter = this;\r
4076 }\r
4077};\r
4078\r
4079\r
4080SenchaTestRunner.Reporter.prototype = {\r
4081 initContextMapping: function() {\r
4082// var xhr = new jasmine.XmlHttpRequest();\r
4083// window.alert(SenchaTestRunner.testArtifactServerURL + "context-directory-mapping");\r
4084// xhr.open("GET", SenchaTestRunner.testArtifactServerURL + "context-directory-mapping", false);\r
4085// xhr.send(null);\r
4086// if (xhr.status === 200) {\r
4087// jasmine.contextMapping = JSON.parse(xhr.responseText);\r
4088// } else {\r
4089// jasmine.contextMapping = null;\r
4090// }\r
4091 },\r
4092 \r
4093 reportRunnerStarting: function() {\r
4094 this.initContextMapping();\r
4095 this.isRunning = true;\r
4096 },\r
4097 \r
4098 reportRunnerResults: function() {\r
4099 this.isRunning = false;\r
4100 Ext.cmd.api.adapter.onTestsDone();\r
4101 },\r
4102 \r
4103 extractRe: /((http:\/\/|file:\/\/\/).*\.js)[^:]*:(\d*)/,\r
4104\r
4105 extractFileAndLine: function(line) {\r
4106 var result = line.match(this.extractRe);\r
4107\r
4108 if (!result) {\r
4109 return null;\r
4110 }\r
4111\r
4112 return {\r
4113 fileName : result[1],\r
4114 lineNumber : parseInt(result[3], 10)\r
4115 };\r
4116 },\r
4117 \r
4118 extractStackTrace: function(error) {\r
4119 var stack = error.stack || error.stackTrace,\r
4120 results = [],\r
4121 lines, line, length, i, extract, fileName, lineNumber;\r
4122\r
4123 if (stack) {\r
4124 lines = stack.split("\n");\r
4125 length = lines.length;\r
4126 for (i = 0; i < length; i++) {\r
4127 line = lines[i];\r
4128 if (line.search(jasmine.util.getOrigin() + "/jasmine.js") === -1) {\r
4129 extract = this.extractFileAndLine(line);\r
4130 if (extract) {\r
4131 results.push(extract);\r
4132 }\r
4133 }\r
4134 }\r
4135 } else {\r
4136 fileName = error.sourceURL || error.fileName;\r
4137 lineNumber = error.line || error.lineNumber;\r
4138\r
4139 if (fileName && lineNumber) {\r
4140 results.push({\r
4141 fileName : fileName,\r
4142 lineNumber : lineNumber\r
4143 });\r
4144 }\r
4145 }\r
4146 return results;\r
4147 },\r
4148 \r
4149 filterRe: /\n|\t|\r|\v|'|"|="|=/g,\r
4150 \r
4151 filterChars: function(string) {\r
4152 return jasmine.util.htmlEscape(string).replace(this.filterRe, ' ');\r
4153 },\r
4154\r
4155 reportSpecStarting: function(spec) {\r
4156 var me = this,\r
4157 testResult = {\r
4158 startTime: (new Date).getTime()\r
4159 };\r
4160\r
4161 spec.startTime = (new Date).getTime();\r
4162 spec._testResult = testResult;\r
4163 },\r
4164\r
4165 reportSpecResults: function(spec) {\r
4166 var blocksArray = [],\r
4167 blocks = spec.queue.blocks,\r
4168 block,\r
4169 results, \r
4170 result, \r
4171 length, \r
4172 i, \r
4173 expectation,\r
4174 testResult = spec._testResult,\r
4175 suiteTestResult = spec.suite && spec.suite._testResult;\r
4176\r
4177 this.checkForCleanup(spec);\r
4178\r
4179 testResult.description = this.filterChars(spec.description);\r
4180 testResult.hash = '' + spec.id;\r
4181 testResult.fileName = spec.fileName;\r
4182 testResult.expectations = [];\r
4183 testResult.duration = (new Date).getTime() - testResult.startTime;\r
4184 testResult.passed = spec.results().passed();\r
4185\r
4186 if (!testResult.passed) {\r
4187 if(suiteTestResult) {\r
4188 suiteTestResult.failures++;\r
4189 suiteTestResult.passed = false;\r
4190 }\r
4191 }\r
4192\r
4193 results = spec.results().getItems();\r
4194\r
4195 length = results.length;\r
4196 for (i = 0; i < length; i++) {\r
4197 result = results[i];\r
4198\r
4199 if (result.type === 'expect') {\r
4200\r
4201 expectation = {\r
4202 description: result.message\r
4203 };\r
4204\r
4205 if (!result.passed() && result.error) {\r
4206 expectation.stackTrace = this.extractStackTrace(result.error);\r
4207 } \r
4208\r
4209 if (result.passed()) {\r
4210 expectation.passed = true;\r
4211 } else {\r
4212 expectation.passed = false;\r
4213 }\r
4214\r
4215 testResult.expectations.push(expectation);\r
4216 }\r
4217 }\r
4218\r
4219 length = blocks.length;\r
4220 i = 0;\r
4221 for (; i < length; i++) {\r
4222 block = blocks[i];\r
4223 if (block.func && block.func.typeName) {\r
4224 blocksArray.push({\r
4225 idx: i,\r
4226 fn: block.func.toString(),\r
4227 typeName: block.func.typeName\r
4228 });\r
4229 }\r
4230 }\r
4231\r
4232 testResult.blocks = blocksArray;\r
4233 Ext.cmd.api.adapter.onTestResult(testResult);\r
4234 delete spec._testResult;\r
4235 },\r
4236 \r
4237 reportSuiteStarting: function(suite) {\r
4238 var testResult = {\r
4239 startTime: (new Date).getTime(),\r
4240 failures: 0,\r
4241 passed: true\r
4242 };\r
4243 \r
4244 suite._testResult = testResult;\r
4245 },\r
4246 \r
4247 reportSuiteResults: function(suite) {\r
4248 var testResult = suite._testResult,\r
4249 parentTestResult = suite.parentSuite && suite.parentSuite._testResult;\r
4250\r
4251 testResult.description = this.filterChars(suite.description);\r
4252 testResult.hash = '' + suite.id;\r
4253 testResult.duration = (new Date).getTime() - testResult.startTime;\r
4254 \r
4255 if (parentTestResult) {\r
4256 parentTestResult.failures += testResult.failures;\r
4257 if (!testResult.passed) {\r
4258 parentTestResult.passed = false;\r
4259 }\r
4260 }\r
4261\r
4262 Ext.cmd.api.adapter.onTestResult(testResult);\r
4263 delete suite._testResult;\r
4264 },\r
4265\r
4266 checkDom: function(spec) {\r
4267 var body = document.body,\r
4268 children = body && body.childNodes || [],\r
4269 len = children.length,\r
4270 badNodes = [],\r
4271 i = 0;\r
4272\r
4273 for (; i < len; i++) {\r
4274 if (children[i].nodeType === 3 || !children[i].getAttribute('data-sticky')) {\r
4275 badNodes.push(children[i]);\r
4276 }\r
4277 }\r
4278\r
4279 for (i = 0, len = badNodes.length; i < len; i++) {\r
4280 document.body.removeChild(badNodes[i]);\r
4281 }\r
4282\r
4283 if (badNodes.length) {\r
4284 spec.fail('document.body contains childNodes after spec execution');\r
4285 }\r
4286 },\r
4287\r
4288 checkForCleanup: function(spec) {\r
4289 this.checkDom(spec);\r
4290 SenchaTestRunner.checkGlobals(spec);\r
4291 }\r
4292};\r
4293\r
4294// The initGlobals() method adds all currently enumerable window properties to a list of\r
4295// allowed globals. It needs to be called after Ext and jasmine are loaded, but before any\r
4296// of the spec files are executed. This ensures that bad globals defined in the spec\r
4297// files do not get added to the list of allowed globals.\r
4298\r
4299\r
4300\r
4301SenchaTestRunner.initGlobals();\r