]> git.proxmox.com Git - extjs.git/blame - extjs/examples/classic/ticket-app/test/local/reporter.js
add extjs 6.0.1 sources
[extjs.git] / extjs / examples / classic / ticket-app / test / local / reporter.js
CommitLineData
6527f429
DM
1var Test = {};var 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
646/**\r
647 * @namespace\r
648 */\r
649jasmine.util = {};\r
650\r
651/**\r
652 * Declare that a child class inherit it's prototype from the parent class.\r
653 *\r
654 * @private\r
655 * @param {Function} childClass\r
656 * @param {Function} parentClass\r
657 */\r
658jasmine.util.inherit = function(childClass, parentClass) {\r
659 /**\r
660 * @private\r
661 */\r
662 var subclass = function() {\r
663 };\r
664 subclass.prototype = parentClass.prototype;\r
665 childClass.prototype = new subclass();\r
666};\r
667\r
668jasmine.util.formatException = function(e) {\r
669 var lineNumber;\r
670 if (e.line) {\r
671 lineNumber = e.line;\r
672 }\r
673 else if (e.lineNumber) {\r
674 lineNumber = e.lineNumber;\r
675 }\r
676\r
677 var file;\r
678\r
679 if (e.sourceURL) {\r
680 file = e.sourceURL;\r
681 }\r
682 else if (e.fileName) {\r
683 file = e.fileName;\r
684 }\r
685\r
686 var message = (e.name && e.message) ? (e.name + ': ' + e.message) : e.toString();\r
687\r
688 if (file && lineNumber) {\r
689 message += ' in ' + file + ' (line ' + lineNumber + ')';\r
690 }\r
691\r
692 return message;\r
693};\r
694\r
695jasmine.util.htmlEscape = function(str) {\r
696 if (!str) return str;\r
697 return str.replace(/&/g, '&amp;')\r
698 .replace(/</g, '&lt;')\r
699 .replace(/>/g, '&gt;');\r
700};\r
701\r
702jasmine.util.argsToArray = function(args) {\r
703 var arrayOfArgs = [];\r
704 for (var i = 0; i < args.length; i++) arrayOfArgs.push(args[i]);\r
705 return arrayOfArgs;\r
706};\r
707\r
708jasmine.util.extend = function(destination, source) {\r
709 for (var property in source) destination[property] = source[property];\r
710 return destination;\r
711};\r
712\r
713/**\r
714 * Base class for pretty printing for expectation results.\r
715 */\r
716jasmine.PrettyPrinter = function() {\r
717 this.ppNestLevel_ = 0;\r
718};\r
719\r
720/**\r
721 * Formats a value in a nice, human-readable string.\r
722 *\r
723 * @param value\r
724 */\r
725jasmine.PrettyPrinter.prototype.format = function(value) {\r
726 this.ppNestLevel_++;\r
727 try {\r
728 if (value === jasmine.undefined) {\r
729 this.emitScalar('undefined');\r
730 } else if (value === null) {\r
731 this.emitScalar('null');\r
732 } else if (value === jasmine.getGlobal()) {\r
733 this.emitScalar('<global>');\r
734 } else if (value.jasmineToString) {\r
735 this.emitScalar(value.jasmineToString());\r
736 } else if (typeof value === 'string') {\r
737 this.emitString(value);\r
738 } else if (jasmine.isSpy(value)) {\r
739 this.emitScalar("spy on " + value.identity);\r
740 } else if (value instanceof RegExp) {\r
741 this.emitScalar(value.toString());\r
742 } else if (typeof value === 'function') {\r
743 this.emitScalar('Function');\r
744 } else if (typeof value.nodeType === 'number') {\r
745 this.emitScalar('HTMLNode');\r
746 } else if (value instanceof Date) {\r
747 this.emitScalar('Date(' + value + ')');\r
748 } else if (value.__Jasmine_been_here_before__) {\r
749 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');\r
750 } else if (jasmine.isArray_(value) || typeof value == 'object') {\r
751 value.__Jasmine_been_here_before__ = true;\r
752 if (jasmine.isArray_(value)) {\r
753 this.emitArray(value);\r
754 } else {\r
755 this.emitObject(value);\r
756 }\r
757 delete value.__Jasmine_been_here_before__;\r
758 } else {\r
759 this.emitScalar(value.toString());\r
760 }\r
761 } finally {\r
762 this.ppNestLevel_--;\r
763 }\r
764};\r
765\r
766jasmine.PrettyPrinter.prototype.iterateObject = function(obj, fn) {\r
767 for (var property in obj) {\r
768 if (!obj.hasOwnProperty(property)) continue;\r
769 if (property == '__Jasmine_been_here_before__') continue;\r
770 fn(property, obj.__lookupGetter__ ? (obj.__lookupGetter__(property) !== jasmine.undefined && \r
771 obj.__lookupGetter__(property) !== null) : false);\r
772 }\r
773};\r
774\r
775jasmine.PrettyPrinter.prototype.emitArray = jasmine.unimplementedMethod_;\r
776jasmine.PrettyPrinter.prototype.emitObject = jasmine.unimplementedMethod_;\r
777jasmine.PrettyPrinter.prototype.emitScalar = jasmine.unimplementedMethod_;\r
778jasmine.PrettyPrinter.prototype.emitString = jasmine.unimplementedMethod_;\r
779\r
780jasmine.StringPrettyPrinter = function() {\r
781 jasmine.PrettyPrinter.call(this);\r
782\r
783 this.string = '';\r
784};\r
785jasmine.util.inherit(jasmine.StringPrettyPrinter, jasmine.PrettyPrinter);\r
786\r
787jasmine.StringPrettyPrinter.prototype.emitScalar = function(value) {\r
788 this.append(value);\r
789};\r
790\r
791jasmine.StringPrettyPrinter.prototype.emitString = function(value) {\r
792 this.append("'" + value + "'");\r
793};\r
794\r
795jasmine.StringPrettyPrinter.prototype.emitArray = function(array) {\r
796 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
797 this.append("Array");\r
798 return;\r
799 }\r
800\r
801 this.append('[ ');\r
802 for (var i = 0; i < array.length; i++) {\r
803 if (i > 0) {\r
804 this.append(', ');\r
805 }\r
806 this.format(array[i]);\r
807 }\r
808 this.append(' ]');\r
809};\r
810\r
811jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {\r
812 if (this.ppNestLevel_ > jasmine.MAX_PRETTY_PRINT_DEPTH) {\r
813 this.append("Object");\r
814 return;\r
815 }\r
816\r
817 var self = this;\r
818 this.append('{ ');\r
819 var first = true;\r
820\r
821 this.iterateObject(obj, function(property, isGetter) {\r
822 if (first) {\r
823 first = false;\r
824 } else {\r
825 self.append(', ');\r
826 }\r
827\r
828 self.append(property);\r
829 self.append(' : ');\r
830 if (isGetter) {\r
831 self.append('<getter>');\r
832 } else {\r
833 self.format(obj[property]);\r
834 }\r
835 });\r
836\r
837 this.append(' }');\r
838};\r
839\r
840jasmine.StringPrettyPrinter.prototype.append = function(value) {\r
841 this.string += value;\r
842};\r
843/**\r
844 * Formats a value in a nice, human-readable string.\r
845 *\r
846 * @param value\r
847 */\r
848jasmine.PrettyPrinter.prototype.format = function(value) {\r
849 if (this.ppNestLevel_ > 40) {\r
850 throw new Error('jasmine.PrettyPrinter: format() nested too deeply!');\r
851 }\r
852\r
853 this.ppNestLevel_++;\r
854 try {\r
855 if (value === jasmine.undefined) {\r
856 this.emitScalar('undefined');\r
857 } else if (value === null) {\r
858 this.emitScalar('null');\r
859 } else if (value === jasmine.getGlobal()) {\r
860 this.emitScalar('<global>');\r
861 } else if (value.expectedClass) { //override of value instanceof jasmine.Matchers.Any\r
862 this.emitScalar(value.toString());\r
863 } else if (typeof value === 'string') {\r
864 this.emitString(value);\r
865 } else if (jasmine.isSpy(value)) {\r
866 this.emitScalar("spy on " + value.identity);\r
867 } else if (value instanceof RegExp) {\r
868 this.emitScalar(value.toString());\r
869 } else if (typeof value === 'function') {\r
870 this.emitScalar('Function');\r
871 } else if (typeof value.nodeType === 'number') {\r
872 this.emitScalar('HTMLNode');\r
873 } else if (value instanceof Date) {\r
874 this.emitScalar('Date(' + value + ')');\r
875 } else if (value.__Jasmine_been_here_before__) {\r
876 this.emitScalar('<circular reference: ' + (jasmine.isArray_(value) ? 'Array' : 'Object') + '>');\r
877 } else if (jasmine.isArray_(value) || typeof value == 'object') {\r
878 value.__Jasmine_been_here_before__ = true;\r
879 if (jasmine.isArray_(value)) {\r
880 this.emitArray(value);\r
881 } else {\r
882 this.emitObject(value);\r
883 }\r
884 delete value.__Jasmine_been_here_before__;\r
885 } else {\r
886 this.emitScalar(value.toString());\r
887 }\r
888 } catch (e) {\r
889 } finally {\r
890 this.ppNestLevel_--;\r
891 }\r
892};\r
893\r
894\r
895// Extend: creates whitespaces indent\r
896jasmine.StringPrettyPrinter.prototype.getIndent = function () {\r
897 var whiteSpaces = "",\r
898 i;\r
899 \r
900 for (i = 0; i < this.ws; i++) {\r
901 whiteSpaces += " ";\r
902 }\r
903\r
904 return whiteSpaces;\r
905};\r
906\r
907// Override: pre-format object\r
908jasmine.StringPrettyPrinter.prototype.emitObject = function(obj) {\r
909 var self = this,\r
910 first = true,\r
911 indent;\r
912 \r
913 this.append('{\n');\r
914 if(!this.ws) {\r
915 this.ws = 0;\r
916 }\r
917 this.ws += 4;\r
918 indent = this.getIndent();\r
919 var i = 0;\r
920 this.iterateObject(obj, function(property, isGetter) {\r
921 \r
922 if (first) {\r
923 first = false;\r
924 } else {\r
925 self.append(',\n');\r
926 }\r
927\r
928 self.append(indent + property);\r
929 self.append(' : ');\r
930 if (isGetter) {\r
931 self.append('<getter>');\r
932 } else {\r
933 if (typeof obj[property] !== "object") {\r
934 self.format(obj[property]); \r
935 } else {\r
936 self.append("<Object>");\r
937 }\r
938 }\r
939 });\r
940 \r
941 this.ws -= 4;\r
942 indent = this.getIndent();\r
943 \r
944 this.append(indent + '\n'+ indent +'}');\r
945\r
946};\r
947/**\r
948 * Basic browsers detection.\r
949 */\r
950jasmine.browser = {};\r
951jasmine.browser.isIE = !!window.ActiveXObject;\r
952jasmine.browser.isIE6 = jasmine.browser.isIE && !window.XMLHttpRequest;\r
953jasmine.browser.isIE7 = jasmine.browser.isIE && !!window.XMLHttpRequest && !document.documentMode;\r
954jasmine.browser.isIE8 = jasmine.browser.isIE && !!window.XMLHttpRequest && !!document.documentMode && !window.performance;\r
955jasmine.browser.isIE9 = jasmine.browser.isIE && !!window.performance;\r
956jasmine.browser.isSafari3 = /safari/.test(navigator.userAgent.toLowerCase()) && /version\/3/.test(navigator.userAgent.toLowerCase());\r
957jasmine.browser.isOpera = !!window.opera;\r
958jasmine.browser.isOpera11 = jasmine.browser.isOpera && parseInt(window.opera.version(), 10) > 10;\r
959jasmine.array = {};\r
960\r
961 /**\r
962 * Checks whether or not the specified item exists in the array.\r
963 * Array.prototype.indexOf is missing in Internet Explorer, unfortunately.\r
964 * We always have to use this static method instead for consistency\r
965 * @param {Array} array The array to check\r
966 * @param {Mixed} item The item to look for\r
967 * @param {Number} from (Optional) The index at which to begin the search\r
968 * @return {Number} The index of item in the array (or -1 if it is not found)\r
969 */\r
970jasmine.array.indexOf = function(array, item, from){\r
971 if (array.indexOf) {\r
972 return array.indexOf(item, from);\r
973 }\r
974 \r
975 var i, length = array.length;\r
976\r
977 for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++){\r
978 if (array[i] === item) {\r
979 return i;\r
980 }\r
981 }\r
982\r
983 return -1;\r
984};\r
985\r
986 /**\r
987 * Removes the specified item from the array. If the item is not found nothing happens.\r
988 * @param {Array} array The array\r
989 * @param {Mixed} item The item to remove\r
990 * @return {Array} The passed array itself\r
991 */\r
992jasmine.array.remove = function(array, item) {\r
993 var index = this.indexOf(array, item);\r
994\r
995 if (index !== -1) {\r
996 array.splice(index, 1);\r
997 }\r
998 \r
999 return array;\r
1000};/**\r
1001 * Creates an HTMLElement.\r
1002 * @param {Object/HTMLElement} config Ext DomHelper style element config object.\r
1003 * If no tag is specified (e.g., {tag:'input'}) then a div will be automatically generated with the specified attributes.\r
1004 * @return {HTMLElement} The created HTMLElement\r
1005 */\r
1006jasmine.Dom = function(config) {\r
1007 var element, children, length, child, i, property;\r
1008 \r
1009 config = config || {};\r
1010 \r
1011 if (config.tagName) {\r
1012 return config;\r
1013 }\r
1014 \r
1015 element = document.createElement(config.tag || "div");\r
1016 children = config.children || [];\r
1017 length = children.length;\r
1018\r
1019 delete config.tag;\r
1020 \r
1021 for (i = 0; i < length; i++) {\r
1022 child = children[i];\r
1023 element.appendChild(new jasmine.Dom(child));\r
1024 }\r
1025 delete config.children;\r
1026 \r
1027 if (config.cls) {\r
1028 jasmine.Dom.setCls(element, config.cls);\r
1029 delete config.cls;\r
1030 }\r
1031\r
1032 if (config.html) {\r
1033 jasmine.Dom.setHTML(element, config.html);\r
1034 delete config.html;\r
1035 }\r
1036\r
1037 if (config.style) {\r
1038 jasmine.Dom.setStyle(element, config.style);\r
1039 delete config.style;\r
1040 }\r
1041 \r
1042 for (property in config) {\r
1043 if (!config.hasOwnProperty(property)) {\r
1044 continue;\r
1045 }\r
1046 element[property] = config[property];\r
1047 }\r
1048\r
1049 return element;\r
1050};\r
1051\r
1052/**\r
1053 * Adds className to an HTMLElement.\r
1054 * @param {HTMLElement} element The HTMLElement\r
1055 * @param {String} cls The className string\r
1056 */\r
1057jasmine.Dom.addCls = function (element, cls) {\r
1058 var split, length, i;\r
1059 \r
1060 if (!element.className) {\r
1061 jasmine.Dom.setCls(element, cls);\r
1062 return;\r
1063 }\r
1064 \r
1065 split = element.className.split(" ");\r
1066 length = split.length;\r
1067 \r
1068 for (i = 0; i < length; i++) {\r
1069 if (split[i] == cls) {\r
1070 return;\r
1071 }\r
1072 }\r
1073 \r
1074 element.className = element.className + " " + cls;\r
1075};\r
1076\r
1077/**\r
1078 * Removes className to HTMLElement.\r
1079 * @param {HTMLElement} element The HTMLElement\r
1080 * @param {String} cls The className string\r
1081 */\r
1082jasmine.Dom.removeCls = function(element, cls) {\r
1083 var split, length, classArray, i;\r
1084 \r
1085 if (!element.className) {\r
1086 return;\r
1087 }\r
1088 \r
1089 classArray = [];\r
1090 split = element.className.split(" ");\r
1091 length = split.length;\r
1092 \r
1093 for (i = 0; i < length; i++) {\r
1094 if (split[i] !== cls) {\r
1095 classArray.push(split[i]);\r
1096 }\r
1097 }\r
1098 \r
1099 element.className = classArray.join(" "); \r
1100};\r
1101\r
1102/**\r
1103 * Checks if a dom element has a className.\r
1104 * @param {HTMLElement} element The HTMLElement\r
1105 * @param {String} cls The className string\r
1106 * @return {Boolean}\r
1107 */\r
1108jasmine.Dom.hasCls = function(element, cls) {\r
1109 var split, length, classArray, i;\r
1110 \r
1111 if (!element.className) {\r
1112 return;\r
1113 }\r
1114 \r
1115 split = element.className.split(" ");\r
1116 length = split.length;\r
1117 \r
1118 for (i = 0; i < length; i++) {\r
1119 if (split[i] === cls) {\r
1120 return true;\r
1121 }\r
1122 }\r
1123 \r
1124 return false; \r
1125};\r
1126\r
1127/**\r
1128 * Sets HTMLElement className.\r
1129 * @param {HTMLElement} element The HTMLElement\r
1130 * @param {String} cls The className string\r
1131 */\r
1132jasmine.Dom.setCls = function(element, cls) {\r
1133 element.className = cls;\r
1134};\r
1135\r
1136/**\r
1137 * Sets HTMLElement innerHTML\r
1138 * @param {HTMLElement} element The HTMLElement\r
1139 * @param {String} html The innerHTML text\r
1140 */\r
1141jasmine.Dom.setHTML = function(element, html) {\r
1142 element.innerHTML = html;\r
1143};\r
1144\r
1145/**\r
1146 * Sets HTMLElement style\r
1147 * @param {HTMLElement} element The HTMLElement\r
1148 * @param {String} style The style property to set\r
1149 */\r
1150jasmine.Dom.setStyle = function(element, style) {\r
1151 var property;\r
1152 for (property in style) {\r
1153 if (style.hasOwnProperty(property)) {\r
1154 element.style[property] = style[property];\r
1155 }\r
1156 }\r
1157};\r
1158Test.OptionsImpl = function() {\r
1159 this.optionCheckBoxesEl = {};\r
1160 this.options = this.urlDecode(window.location.search.substring(1));\r
1161 this.options.remote = window.location.toString().search("http:") !== -1;\r
1162 this.startAutoReloadTask();\r
1163 \r
1164};\r
1165\r
1166Test.OptionsImpl.prototype.get = function() {\r
1167 return this.options;\r
1168};\r
1169\r
1170/**\r
1171 * Takes an object and converts it to an encoded URL.\r
1172 * @param {Object} o The object to encode\r
1173 * @return {String}\r
1174 */\r
1175Test.OptionsImpl.prototype.urlEncode = function(object) {\r
1176 var buf = [],\r
1177 e = encodeURIComponent,\r
1178 value, property, length, i;\r
1179\r
1180 for (property in object) {\r
1181 if(!object.hasOwnProperty(property)) {\r
1182 continue;\r
1183 }\r
1184 value = object[property];\r
1185 if (jasmine.isArray_(value)) {\r
1186 length = value.length;\r
1187 for (i = 0; i < length; i++) {\r
1188 buf.push(property + '=' + e(value[i]));\r
1189 }\r
1190 } else {\r
1191 buf.push(property + '=' + e(value));\r
1192 }\r
1193 }\r
1194 return buf.join('&');\r
1195};\r
1196\r
1197Test.hashString = function (s, hash) {\r
1198 hash = hash || 0;\r
1199\r
1200 // see http://www.cse.yorku.ca/~oz/hash.html\r
1201 for (var c, i = 0, n = s.length; i < n; ++i) {\r
1202 c = s.charCodeAt(i);\r
1203 hash = c + (hash << 6) + (hash << 16) - hash;\r
1204 }\r
1205\r
1206 return hash;\r
1207};\r
1208\r
1209/**\r
1210 * Takes an encoded URL and and converts it to an object. Example:\r
1211 * @param {String} string\r
1212 * @return {Object} A literal with members\r
1213 */\r
1214Test.OptionsImpl.prototype.urlDecode = function(string) {\r
1215 var obj = {},\r
1216 pairs, d, name, value, pair, i, length;\r
1217 \r
1218 if (string != "") {\r
1219 pairs = string.split('&');\r
1220 d = decodeURIComponent;\r
1221 length = pairs.length;\r
1222 for (i = 0; i < length; i++) {\r
1223 pair = pairs[i].split('=');\r
1224 name = d(pair[0]);\r
1225 value = d(pair[1]);\r
1226 obj[name] = !obj[name] ? value : [].concat(obj[name]).concat(value);\r
1227 }\r
1228 }\r
1229 function parseStringOrId (str) {\r
1230 var id = parseInt(str, 10);\r
1231 if (String(id) !== str) {\r
1232 id = Test.hashString(str);\r
1233 }\r
1234 return id;\r
1235 }\r
1236 \r
1237 if (obj.specs) {\r
1238 obj.specs = jasmine.isArray_(obj.specs) ? obj.specs : [obj.specs];\r
1239 length = obj.specs.length;\r
1240 for (i = 0; i < length; i++) {\r
1241 obj.specs[i] = parseStringOrId(obj.specs[i]);\r
1242 }\r
1243 } else {\r
1244 obj.specs = [];\r
1245 }\r
1246 \r
1247 if (obj.suites) {\r
1248 obj.suites = jasmine.isArray_(obj.suites) ? obj.suites : [obj.suites];\r
1249 length = obj.suites.length;\r
1250 for (i = 0; i < length; i++) {\r
1251 obj.suites[i] = parseStringOrId(obj.suites[i]);\r
1252 }\r
1253 } else {\r
1254 obj.suites = [];\r
1255 }\r
1256 \r
1257 return obj;\r
1258};\r
1259\r
1260/**\r
1261 * Renders option checkbox and label.\r
1262 * @param {String} name The option name.\r
1263 * @param {String} labelText The label text.\r
1264 * @return {HTMLElement} The option HTMLElement\r
1265 */ \r
1266Test.OptionsImpl.prototype.renderCheckbox = function(name, labelText) {\r
1267 var me = this,\r
1268 checkbox = new jasmine.Dom({\r
1269 tag: "input",\r
1270 cls: "option " + name,\r
1271 type: "checkbox",\r
1272 onclick: function() {\r
1273 me.onCheckboxClick.apply(me, arguments);\r
1274 }\r
1275 });\r
1276 \r
1277 me.optionCheckBoxesEl[name] = checkbox; \r
1278 \r
1279 return new jasmine.Dom({\r
1280 tag: "span",\r
1281 cls: "show",\r
1282 children: [checkbox,{\r
1283 tag: "label",\r
1284 html: labelText\r
1285 }]\r
1286 });\r
1287};\r
1288\r
1289/**\r
1290 * Checks options checkboxs if needed. \r
1291 */\r
1292Test.OptionsImpl.prototype.check = function() {\r
1293 var property, checkbox;\r
1294 \r
1295 for (property in this.options) {\r
1296 if (!this.options.hasOwnProperty(property)) {\r
1297 continue;\r
1298 }\r
1299 checkbox = this.optionCheckBoxesEl[property];\r
1300 if (checkbox) {\r
1301 checkbox.checked = this.options[property];\r
1302 }\r
1303 }\r
1304};\r
1305\r
1306/**\r
1307 * Options checkbox check/uncked handler.\r
1308 * @param {HTMLElement} el The checkbox HTMLElement\r
1309 */\r
1310Test.OptionsImpl.prototype.onCheckboxClick = function(event) {\r
1311 var el, opt, row, length, i;\r
1312 event = event || window.event;\r
1313 el = event.target || event.srcElement;\r
1314 opt = el.className.split(" ")[1];\r
1315 if (el.checked) { \r
1316 this.options[opt] = true;\r
1317 } else {\r
1318 delete this.options[opt];\r
1319 }\r
1320};\r
1321\r
1322/**\r
1323 * Reloads current page with reporter options.\r
1324 */\r
1325Test.OptionsImpl.prototype.reloadWindow = function(reset) {\r
1326 if (reset) {\r
1327 this.options.specs = [];\r
1328 this.options.suites = [];\r
1329 }\r
1330\r
1331 window.location.search = this.urlEncode(this.options);\r
1332};\r
1333\r
1334/**\r
1335 * Starts autoReload task.\r
1336 */\r
1337Test.OptionsImpl.prototype.startAutoReloadTask = function() {\r
1338 var me = this;\r
1339 if (me.options.autoReload) {\r
1340 var interval = setInterval(function() {\r
1341 if (Test.SandBox.isRunning()) {\r
1342 clearInterval(interval);\r
1343 \r
1344 setTimeout(function() {\r
1345 me.reloadWindow();\r
1346 }, 2000);\r
1347 }\r
1348 }, 1500);\r
1349 }\r
1350};\r
1351\r
1352Test.OptionsImpl.prototype.isChecked = function(o) {\r
1353 var specs = this.options.specs,\r
1354 suites = this.options.suites,\r
1355 id = o.id;\r
1356\r
1357 if (o.suite) {\r
1358 return specs && jasmine.array.indexOf(specs, id) !== -1;\r
1359 } else {\r
1360 return suites && jasmine.array.indexOf(suites, id) !== -1;\r
1361 }\r
1362\r
1363 return false;\r
1364};\r
1365\r
1366Test.Options = new Test.OptionsImpl();Test.SandBoxImpl = function(){};\r
1367\r
1368Test.SandBoxImpl.prototype.domReady = function(fn) {\r
1369 if (document.addEventListener) {\r
1370 window.addEventListener('load', fn, false);\r
1371 } else {\r
1372 window.attachEvent('onload', fn, false);\r
1373 }\r
1374}; \r
1375\r
1376Test.SandBoxImpl.prototype.setup = function(config) {\r
1377 var me = this;\r
1378 me.requires = config.requires; \r
1379 me.domReady(function() {\r
1380 me.reporter = new Test.Reporter();\r
1381 me.createIframe();\r
1382 });\r
1383};\r
1384\r
1385Test.SandBoxImpl.prototype.createIframe = function() {\r
1386 var me = this,\r
1387 iframe,\r
1388 win,\r
1389 doc;\r
1390\r
1391 me.options = Test.Options.get();\r
1392\r
1393\r
1394 var src = me.options.quirksMode ? 'iframe-quirks.html?loadSpecs=true' : 'iframe.html?loadSpecs=true';\r
1395 \r
1396 src += '&compiled=' + !!me.options.compiled;\r
1397\r
1398 if (me.options.specsset) {\r
1399 src += '&specsset=' + me.options.specsset;\r
1400 }\r
1401 \r
1402 iframe = new jasmine.Dom({\r
1403 tag: "iframe",\r
1404 cls: "sandboxIframe",\r
1405 name: "sandbox",\r
1406 frameBorder: 0,\r
1407 src: src\r
1408 });\r
1409\r
1410 me.reporter.getIframeContainer().appendChild(iframe);\r
1411 \r
1412 win = iframe.contentWindow || window.frames[iframe.name];\r
1413 doc = iframe.contentDocument || win.document;\r
1414 this.iframe = iframe;\r
1415 this.win = win;\r
1416 this.doc = doc;\r
1417};\r
1418\r
1419Test.SandBoxImpl.prototype.getIframe = function() {\r
1420 return this.iframe;\r
1421};\r
1422\r
1423Test.SandBoxImpl.prototype.getWin = function() {\r
1424 return this.win;\r
1425};\r
1426\r
1427Test.SandBoxImpl.prototype.getDoc = function() {\r
1428 return this.doc;\r
1429};\r
1430\r
1431Test.SandBoxImpl.prototype.getBody = function() {\r
1432 return this.getDoc().body;\r
1433};\r
1434\r
1435Test.SandBoxImpl.prototype.getHead = function() {\r
1436 return this.getDoc().getElementsByTagName("head")[0];\r
1437};\r
1438\r
1439Test.SandBoxImpl.prototype.save = function(spec) {\r
1440 var doc = this.getDoc(),\r
1441 sb = doc.createElement("div"),\r
1442 body = this.getBody(),\r
1443 children = body && body.childNodes || [],\r
1444 length = children.length,\r
1445 i = 0,\r
1446 child,\r
1447 lwas = this.lengthWas || (this.lengthWas = 0);\r
1448\r
1449 if (!this.options || !this.options.disableBodyClean) {\r
1450 //this.clearComponents();\r
1451 //this.clearDomElements();\r
1452 }\r
1453\r
1454 if (length != lwas) {\r
1455 if (!window.headless) {\r
1456 this.reporter.log(">> Warning the document.body dom element contains childNodes after spec execution !<br/>" +\r
1457 "Spec : " + jasmine.util.htmlEscape(spec.getFullName()) + ' <a href="?' +\r
1458 Test.Options.urlEncode({specs: [spec.id], suites:[], disableBodyClean: true}) + '">Load this spec only and disable body autoclean</a><br/>',\r
1459 "warning");\r
1460 } else {\r
1461 this.reporter.log("Warning: " + spec.getFullName() + "doesn't clean properly the document.body.");\r
1462 }\r
1463 this.lengthWas = length;\r
1464 }\r
1465\r
1466};\r
1467\r
1468Test.SandBoxImpl.prototype.clearDomElements = function() {\r
1469 var doc = this.getDoc(),\r
1470 bd = this.getBody(),\r
1471 children = bd.childNodes,\r
1472 length = children.length,\r
1473 i, child;\r
1474\r
1475 if (!this.options.disableBodyClean) {\r
1476 for (i = 0; i < length; i++) {\r
1477 child = children[i];\r
1478 if (child) {\r
1479 bd.removeChild(child);\r
1480 }\r
1481 }\r
1482 }\r
1483}\r
1484\r
1485Test.SandBoxImpl.prototype.clearComponents = function() {\r
1486 var me = this,\r
1487 win = me.getWin(),\r
1488 comps, c, len, i;\r
1489\r
1490 if(win.Ext && win.Ext.ComponentManager) {\r
1491 comps = win.Ext.ComponentManager.all.getArray();\r
1492 len = comps.length;\r
1493 for(i=0; i<len; i++) {\r
1494 c = comps[i];\r
1495 c.destroy();\r
1496 }\r
1497 }\r
1498};\r
1499\r
1500Test.SandBoxImpl.prototype.isRunning = function() {\r
1501 return !this.getWin().jasmine.getEnv().currentRunner_.queue.isRunning();\r
1502};\r
1503\r
1504Test.SandBoxImpl.prototype.iScope = function(o) {\r
1505 if (typeof o === "function") {\r
1506 o = "(" + o.toString() + ")();";\r
1507 }\r
1508 return Test.SandBox.getWin().eval(o);\r
1509};\r
1510\r
1511Test.SandBox = new Test.SandBoxImpl();\r
1512var iScope = Test.SandBox.iScope; /**\r
1513 * @class Test.CodeHighLighter\r
1514 * A javascript simple source code higlighter and beautifier (optional).\r
1515 */\r
1516Test.CodeHighLighter = function(config) { \r
1517 /**\r
1518 * @cfg {String} source The source string to process.\r
1519 */\r
1520 this.source = config.source;\r
1521 this.lineNumber = config.lineNumber;\r
1522 this.linesFromJsCoverage = config.linesFromJsCoverage;\r
1523 \r
1524 this.beautify = config.beautify || this.lineNumber === undefined;\r
1525 this.highLightCode = config.highLightCode === false ? false : true;\r
1526 \r
1527 this.matchedComments = [];\r
1528 this.matchedStrings = [];\r
1529};\r
1530\r
1531/**\r
1532 * Regular expressions.\r
1533 */\r
1534Test.CodeHighLighter.prototype.regExps = {\r
1535 strings: /"([^\\"\n]|\\.)*"|'([^\\'\n]|\\.)*'|"([^\\"\n]|\\\n)*"|'([^\\'\n]|\\\n)*'/gm,\r
1536 comments: /\/\/.*$|\/\*[\s\S]*?\*\//gm,\r
1537 operators: /([\+\-\*\/=\?!]{1,3}|[\-\+]{1,2})/g,\r
1538 numbers: /\b([0-9]+)\b/g,\r
1539 keywords: [/\b(break)\b/g, /\b(case)\b/g, /\b(catch)\b/g, /\b(continue)\b/g, /\b(default)\b/g,\r
1540 /\b(delete)\b/g, /\b(do)\b/g, /\b(else)\b/g, /\b(false)\b/g, /\b(for)\b/g, /\b(function)\b/g,\r
1541 /\b(if)\b/g, /\b(in)\b/g, /\b(instanceof)\b/g, /\b(new)\b/g, /\b(null)\b/g,\r
1542 /\b(return)\b/g, /\b(switch)\b/g, /\b(this)\b/g, /\b(throw)\b/g, /\b(true)\b/g,\r
1543 /\b(try)\b/g,/\b(typeof)\b/g, /\b(var)\b/g, /\b(while)\b/g, /\b(with)\b/g],\r
1544 commasInsideParenthesis: /\(([^\(\)\{\}])+\)/g,\r
1545 arrayWithOneElement: /\[\n([^,\]]*)\n\]/g,\r
1546 commaBracket: /,\n\s*\{/g,\r
1547 multipleWhiteSpaces: /(\s+)/g,\r
1548 semiColon: /;/g,\r
1549 comma: /,/g,\r
1550 openedBrackets: /([\{\[])/g,\r
1551 closedBrackets: /([\}\]])/g,\r
1552 emptyObject: /\{\n\s*\n\}/g,\r
1553 openedBracketsWithNewLine: /[\{\[]$/g,\r
1554 closedBracketsWithNewLine: /^\s*[\}\]]/g,\r
1555 unwantedNewLines: /\n([\n,;\)])/g,\r
1556 newLine: /\n/g,\r
1557 firstSpaces: /^(\s)+/\r
1558};\r
1559\r
1560/**\r
1561 * Populates an array of matched objects.\r
1562 * @param {String} value The match result.\r
1563 * @param {Number} index The index of the match.\r
1564 * @param {Array} matchedObjects The array of matches to populate.\r
1565 * @param {String} css The css to apply to the match.\r
1566 * @return {Boolean} Returns <tt>true</tt> is the match is inside another.\r
1567 */\r
1568Test.CodeHighLighter.prototype.matchObjects = function(value, index, matchedObjects, css) {\r
1569 matchedObjects.push({\r
1570 origValue: value,\r
1571 value: '<span class="jsHl'+ css +'">' + jasmine.util.htmlEscape(value).replace("$","$\b") + '</span>',\r
1572 start: index,\r
1573 end: index + value.length\r
1574 });\r
1575};\r
1576\r
1577/**\r
1578 * Checks if a match is inside another matches.\r
1579 * @param {Object} matchedObject The checked match.\r
1580 * @param {Array} matchedOthers The array that contains other matches.\r
1581 * @return {Boolean} Returns <tt>true</tt> is the match is inside another.\r
1582 */\r
1583Test.CodeHighLighter.prototype.isInside = function(matchedObject, matchedOthers) {\r
1584 var start = matchedObject.start,\r
1585 end = matchedObject.end,\r
1586 length = matchedOthers.length,\r
1587 matchedOther, i;\r
1588\r
1589 for (i = 0; i < length; i++) {\r
1590 matchedOther = matchedOthers[i];\r
1591 if (matchedOther.start < start && start < matchedOther.end) {\r
1592 return true;\r
1593 } \r
1594 }\r
1595 return false;\r
1596};\r
1597\r
1598/**\r
1599 * This function get rid of any matches that are inside of other matches.\r
1600 * If a match isn't inside another it is replaced by a string in {@link #source}\r
1601 * in order to protect it from {@link #processOperatorsNumbersKeywords} replace tricks.\r
1602 * @param {Array} matchedObjects The array of matches to check.\r
1603 * @param {Array} matchedOthers The array that contains other matches.\r
1604 * @param {String} protect The replacement string\r
1605 */\r
1606Test.CodeHighLighter.prototype.fixOverlaps = function(matchedObjects, matchedOthers, protect) {\r
1607 var result = [],\r
1608 length = matchedObjects.length,\r
1609 matchedObject,\r
1610 i;\r
1611 \r
1612 for (i = 0; i < length; i++) {\r
1613 matchedObject = matchedObjects[i];\r
1614 if (!this.isInside(matchedObject, matchedOthers)) {\r
1615 this.source = this.source.replace(matchedObject.origValue, protect);\r
1616 result.push(matchedObject);\r
1617 }\r
1618 }\r
1619 return result;\r
1620};\r
1621\r
1622/**\r
1623 * Replaces Strings and Comments in javascript source code.\r
1624 */\r
1625Test.CodeHighLighter.prototype.saveStringsAndComments = function() {\r
1626 var commentsRe = this.regExps.comments,\r
1627 stringsRe = this.regExps.strings,\r
1628 exec;\r
1629 \r
1630 \r
1631 while((exec = commentsRe.exec(this.source))) {\r
1632 this.matchObjects(exec[0], exec.index, this.matchedComments, "Comment");\r
1633 }\r
1634 \r
1635 while((exec = stringsRe.exec(this.source))) {\r
1636 this.matchObjects(exec[0], exec.index, this.matchedStrings, "String");\r
1637 }\r
1638\r
1639 this.matchedComments = this.fixOverlaps(this.matchedComments, this.matchedStrings, "%%%%comment%%%%");\r
1640 this.matchedStrings = this.fixOverlaps(this.matchedStrings, this.matchedComments, '%%%%string%%%%');\r
1641};\r
1642\r
1643/**\r
1644 * Process strings and comments saved by {@link #saveStringsAndComments}.\r
1645 */\r
1646Test.CodeHighLighter.prototype.processStringsAndComments = function() {\r
1647 var matches = this.matchedComments,\r
1648 length = matches ? matches.length : 0,\r
1649 value, i;\r
1650\r
1651 for (i = 0; i < length; i++) {\r
1652 value = matches[i].value;\r
1653 this.source = this.source.replace("%%%%comment%%%%", value);\r
1654 }\r
1655 \r
1656 matches = this.matchedStrings;\r
1657 length = matches ? matches.length : 0;\r
1658 \r
1659 for (i = 0; i < length; i++) {\r
1660 value = matches[i].value;\r
1661 this.source = this.source.replace('%%%%string%%%%', value);\r
1662 }\r
1663};\r
1664\r
1665/**\r
1666 * Highlight operators, numbers and keywords.\r
1667 */\r
1668Test.CodeHighLighter.prototype.processOperatorsNumbersKeywords = function() {\r
1669 var regexps = this.regExps,\r
1670 keywords = regexps.keywords,\r
1671 length = keywords.length,\r
1672 i;\r
1673 \r
1674 this.source = jasmine.util.htmlEscape(this.source).replace(\r
1675 regexps.operators, '<span class="jsHlOperator">$1</span>').replace(\r
1676 regexps.numbers, '<span class="jsHlNumber">$1</span>');\r
1677 \r
1678 for (i = 0; i < length; i++) {\r
1679 this.source = this.source.replace(keywords[i], '<span class="jsHlKeyword">$1</span>');\r
1680 }\r
1681};\r
1682 \r
1683/**\r
1684 * Format and highligth javascript sources.\r
1685 * @return The HTML formatted and highlighted code\r
1686 */\r
1687Test.CodeHighLighter.prototype.process = function() {\r
1688 this.saveStringsAndComments();\r
1689 \r
1690 if (this.beautify) {\r
1691 this.prepareIndent();\r
1692 this.doIndent();\r
1693 }\r
1694 \r
1695 this.processOperatorsNumbersKeywords();\r
1696\r
1697 this.processStringsAndComments();\r
1698\r
1699 return this.source;\r
1700};\r
1701\r
1702/**\r
1703 * Render sources with line numbers.\r
1704 * @return The HTML formatted and highlighted code\r
1705 */\r
1706Test.CodeHighLighter.prototype.renderJsSources = function() {\r
1707 var result = 'No code found.',\r
1708 linesFromJsCoverage = this.linesFromJsCoverage,\r
1709 lineNumber = this.lineNumber,\r
1710 source = this.source,\r
1711 lines, line, i, errorCls, length, lineNumberCls;\r
1712\r
1713 if (source) {\r
1714 source = this.highLightCode ? this.process() : source;\r
1715 lines = source.split("\n");\r
1716 length = lines.length;\r
1717 \r
1718 result = '<table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="lineNumbers">';\r
1719 for (i = 0; i < length; i++) {\r
1720 errorCls = "";\r
1721 lineNumberCls = "";\r
1722 if (lineNumber) {\r
1723 errorCls = i === (lineNumber - 1) ? " error" : "";\r
1724 }\r
1725 if (linesFromJsCoverage) {\r
1726 lineNumberCls = !isNaN(linesFromJsCoverage[i + 1]) ? " lineNumberGreen" : "";\r
1727 lineNumberCls = linesFromJsCoverage[i + 1] === 0 ? " lineNumberRed" : lineNumberCls;\r
1728\r
1729 }\r
1730 result += '<div class="lineNumber' + errorCls + lineNumberCls + '">' + (i + 1) +'</div>';\r
1731 }\r
1732\r
1733 result += '</td><td><pre class="code">'+ source +'</pre></td></tr></tbody></table>';\r
1734 }\r
1735 \r
1736 this.source = result;\r
1737\r
1738 return this.source;\r
1739};\r
1740\r
1741/**\r
1742 * Prepares source code. It crops double whitespace and append new lines.\r
1743 * This function is used generally to preformat the code that come from a \r
1744 * Function.prototype.toString.\r
1745 */\r
1746Test.CodeHighLighter.prototype.prepareIndent = function() {\r
1747 var regexps = this.regExps,\r
1748 matches, length, i, m;\r
1749 \r
1750 this.source = this.source.replace(\r
1751 regexps.multipleWhiteSpaces, " ").replace(\r
1752 regexps.semiColon, ";\n").replace(\r
1753 regexps.comma, ",\n").replace(\r
1754 regexps.openedBrackets, "$1\n").replace(\r
1755 regexps.closedBrackets, "\n$1\n");\r
1756\r
1757 \r
1758 // remove newline after commas inside code parenthesis\r
1759 matches = this.source.match(regexps.commasInsideParenthesis);\r
1760\r
1761 length = matches ? matches.length : 0;\r
1762 for (i = 0; i < length; i++) {\r
1763 m = matches[i];\r
1764 this.source = this.source.replace(m, m.replace(regexps.newLine, ""));\r
1765 }\r
1766 \r
1767 // fixes various bad formatting\r
1768 this.source = this.source.replace(regexps.arrayWithOneElement, "[$1]").replace(\r
1769 regexps.emptyObject, "{}").replace(\r
1770 regexps.commaBracket, ", {").replace(\r
1771 regexps.unwantedNewLines, "$1");\r
1772};\r
1773\r
1774/**\r
1775 * Creates a string composed of n whitespaces\r
1776 * @param {Number} number The number of white spaces.\r
1777 * @return {String} A multiple whitespace string.\r
1778 */\r
1779Test.CodeHighLighter.prototype.addWhiteSpaces = function (number) {\r
1780 var whiteSpaces = "",\r
1781 i;\r
1782 \r
1783 for (i = 0; i < number; i++) {\r
1784 whiteSpaces += " ";\r
1785 }\r
1786\r
1787 return whiteSpaces;\r
1788};\r
1789\r
1790/**\r
1791 * Indents pre-formatted source code.\r
1792 */\r
1793Test.CodeHighLighter.prototype.doIndent = function() {\r
1794 var regexps = this.regExps, \r
1795 results = [],\r
1796 indent = 0,\r
1797 sources = this.source.split("\n"),\r
1798 length = sources.length,\r
1799 whiteSpaces = "",\r
1800 source, i;\r
1801\r
1802 for (i = 0; i < length; i++) {\r
1803 source = sources[i].replace(regexps.firstSpaces, '');\r
1804 if (source !== "") {\r
1805 if (source.search(regexps.closedBracketsWithNewLine) !== -1) {\r
1806 indent = Math.max(indent - 4, 0);\r
1807 whiteSpaces = this.addWhiteSpaces(indent);\r
1808 }\r
1809 results.push(whiteSpaces + source);\r
1810 if (source.search(regexps.openedBracketsWithNewLine) !== -1) {\r
1811 indent += 4;\r
1812 whiteSpaces = this.addWhiteSpaces(indent);\r
1813 }\r
1814 }\r
1815 }\r
1816 this.source = results.join("\n");\r
1817};\r
1818/**\r
1819 * Init allowedGlobals array.\r
1820 */\r
1821Test.BadGlobalsImpl = function(reporter) {\r
1822 this.results = [];\r
1823};\r
1824\r
1825Test.BadGlobalsImpl.prototype.setup = function() {\r
1826 var me = this, \r
1827 win = Test.SandBox.getWin(),\r
1828 property;\r
1829 \r
1830 // whitelist support \r
1831 win.addGlobal = function() {\r
1832 me.addGlobal.apply(me, arguments);\r
1833 };\r
1834 \r
1835 me.allowedGlobals = {};\r
1836 for (property in win) {\r
1837 me.allowedGlobals[property] = true;\r
1838 }\r
1839 // add firebug globals variables to white list\r
1840 me.allowedGlobals._firebug = true;\r
1841 me.allowedGlobals._createFirebugConsole = true;\r
1842 me.allowedGlobals.loadFirebugConsole = true;\r
1843 me.allowedGlobals.console = true;\r
1844};\r
1845\r
1846\r
1847/**\r
1848 * Append to suite HTMLElement warning messages if improper global variables are found.\r
1849 * @param {HTMLElement} suiteEl The suite HTMLElement.\r
1850 */\r
1851Test.BadGlobalsImpl.prototype.report = function(info, suite) {\r
1852 var allowedGlobals = this.allowedGlobals,\r
1853 win = Test.SandBox.getWin(),\r
1854 property, message, value;\r
1855 \r
1856 for (property in win) {\r
1857 if (!allowedGlobals[property]) {\r
1858 value = jasmine.pp(win[property]);\r
1859 message = ">> Bad global variable found in " + (suite ? suite.description : "global scope") + "<br/>" + property + " = " + value;\r
1860 info.log(message, "warning"); \r
1861 this.results[property] = {\r
1862 where: (suite ? ('in suite' + suite.description) : "global scope"),\r
1863 value: value\r
1864 };\r
1865 allowedGlobals[property] = true;\r
1866 }\r
1867 } \r
1868};\r
1869\r
1870Test.BadGlobalsImpl.prototype.addGlobal = function(property) {\r
1871 this.allowedGlobals[property] = true;\r
1872};\r
1873\r
1874if (!jasmine.browser.isIE && !jasmine.browser.isOpera) {\r
1875 Test.BadGlobals = new Test.BadGlobalsImpl();\r
1876}/**\r
1877 * @singleton Test.jsCoverage\r
1878 * The jscoverage manager.\r
1879 */\r
1880Test.jsCoverage = {\r
1881 executed: 0,\r
1882 coverage: {},\r
1883\r
1884 isEnabled: function() {\r
1885 return !!Test.SandBox.getWin()._$jscoverage;\r
1886 },\r
1887\r
1888 getCoverage: function() {\r
1889 return this.coverage;\r
1890 },\r
1891 \r
1892 getSandBoxCoverage: function() {\r
1893 return Test.SandBox.getWin()._$jscoverage;\r
1894 },\r
1895/**\r
1896 * Adds suite to the jscoverage manager.\r
1897 * @param {jasmine.Suite} The jasmine suite.\r
1898 */\r
1899 add: function(suite) {\r
1900 var coverage = this.getSandBoxCoverage(),\r
1901 filename, file, property, statement;\r
1902 \r
1903 if (!coverage) {\r
1904 return;\r
1905 }\r
1906 filename = this.getFileName(suite.coverageFile);\r
1907 file = coverage[filename];\r
1908 if (coverage && file) {\r
1909 for (property in file) {\r
1910 if (!file.hasOwnProperty(property)) {\r
1911 continue;\r
1912 }\r
1913 statement = file[property];\r
1914 }\r
1915 } \r
1916 },\r
1917/**\r
1918 * This methods try to find the corresponding javascript source file.\r
1919 * @param {String} The filename.\r
1920 */\r
1921 getFileName: function(filename) {\r
1922 var coverage = this.getSandBoxCoverage(), \r
1923 property;\r
1924 \r
1925 if (!coverage || !filename) {\r
1926 return;\r
1927 }\r
1928 \r
1929 if (coverage[filename]) {\r
1930 return filename;\r
1931 }\r
1932 \r
1933 for (property in coverage) {\r
1934 if (property.search(filename) !== -1) {\r
1935 return property;\r
1936 }\r
1937 }\r
1938 },\r
1939/**\r
1940 * Updates suite coverage results after execution.\r
1941 * @param {jasmine.Suite} The jasmine suite.\r
1942 */\r
1943 update: function(suite) {\r
1944 var coverage = this.getSandBoxCoverage(),\r
1945 statements = 0,\r
1946 executed = 0,\r
1947 property, statement, filename, file;\r
1948 \r
1949 if (!coverage) {\r
1950 return;\r
1951 }\r
1952 \r
1953 filename = this.getFileName(suite.coverageFile);\r
1954 file = coverage[filename];\r
1955 \r
1956 if (file) {\r
1957 suite.jscoverage = {\r
1958 file: []\r
1959 };\r
1960 \r
1961 for (property in file) {\r
1962 if (!file.hasOwnProperty(property)) {\r
1963 continue;\r
1964 }\r
1965 statement = file[property];\r
1966 \r
1967 suite.jscoverage.file[property] = statement;\r
1968 \r
1969 if (!isNaN(property) && statement !== undefined) {\r
1970 statements = statements + 1;\r
1971 if (statement !== 0) {\r
1972 this.executed = this.executed + 1;\r
1973 executed = executed + 1;\r
1974 }\r
1975 }\r
1976 }\r
1977 suite.jscoverage.percentage = ((executed/statements) * 100).toFixed(2);\r
1978 suite.jscoverage.statements = statements;\r
1979 suite.jscoverage.executed = executed;\r
1980 this.coverage[filename] = suite.jscoverage.file;\r
1981 this.coverage[filename].percentage = suite.jscoverage.percentage;\r
1982 this.coverage[filename].statements = suite.jscoverage.statements;\r
1983 this.coverage[filename].executed = suite.jscoverage.executed;\r
1984 }\r
1985 },\r
1986/**\r
1987 * Returns suite coverage text.\r
1988 * @param {jasmine.Suite} The jasmine suite.\r
1989 * @return {String} The Code coverage text<\r
1990 */\r
1991 getSuiteCoverage: function(suite) {\r
1992 if (suite.jscoverage) {\r
1993 return " - Code coverage: " + suite.jscoverage.percentage + "%";\r
1994 }\r
1995 return '';\r
1996 },\r
1997/**\r
1998 * Gets total code coverage.\r
1999 * @return {String} A string with total code coverage.\r
2000 */\r
2001 getTotal: function() {\r
2002 if (this.percentage) {\r
2003 return " - Code coverage: " + this.percentage + "%";\r
2004 }\r
2005 \r
2006 return '';\r
2007 },\r
2008\r
2009 updateTotal: function() {\r
2010 var coverage = this.getSandBoxCoverage(),\r
2011 statements = 0,\r
2012 file, filename, statement, property, fstatements, fexecuted, create;\r
2013 \r
2014 if(!coverage) {\r
2015 return "";\r
2016 }\r
2017 \r
2018 for (filename in coverage) {\r
2019 if (!coverage.hasOwnProperty(filename)) {\r
2020 continue;\r
2021 }\r
2022 file = coverage[filename];\r
2023 fstatements = 0;\r
2024 fexecuted = 0;\r
2025 \r
2026 create = !this.coverage[filename];\r
2027 if (create) {\r
2028 this.coverage[filename] = [];\r
2029 }\r
2030 for (property in file) {\r
2031 if (!file.hasOwnProperty(property)) {\r
2032 continue;\r
2033 }\r
2034 statement = file[property];\r
2035 \r
2036 if (!isNaN(property)) {\r
2037 if (statement !== undefined) {\r
2038 statements = statements + 1;\r
2039 fstatements = fstatements + 1;\r
2040 }\r
2041 if (create) {\r
2042 this.coverage[filename][property] = 0;\r
2043 }\r
2044 }\r
2045 }\r
2046 \r
2047 if (create) {\r
2048 this.coverage[filename].source = file.source;\r
2049 this.coverage[filename].statements = fstatements;\r
2050 this.coverage[filename].executed = fexecuted;\r
2051 this.coverage[filename].percentage = ((fexecuted/fstatements) * 100).toFixed(2);\r
2052 } \r
2053\r
2054 }\r
2055 this.statements = statements;\r
2056 this.percentage = ((this.executed/statements) * 100).toFixed(2);\r
2057 }\r
2058 \r
2059};Test.panel = {};\r
2060/**\r
2061 * Renders Jasmine Blocks executed by spec.\r
2062 * @param {Jasmine.spec} spec The spec.\r
2063 * @param {HTMLElement} panelsEl The HTMLElement which encapsulate the tools panels.\r
2064 */\r
2065Test.panel.Blocks = function(config) {\r
2066 var blocks = config.spec.queue.blocks,\r
2067 length = blocks.length,\r
2068 cls = "panel blocks",\r
2069 children = [],\r
2070 i, block, codeHighLighter;\r
2071\r
2072 for (i = 0; i < length; i++) {\r
2073 block = blocks[i];\r
2074 if (block.func) {\r
2075 children.push({\r
2076 cls: "blockTitle " + (block.func.typeName || "specSources"),\r
2077 html: block.func.typeName || 'it("' + jasmine.util.htmlEscape(config.spec.description) + '")'\r
2078 });\r
2079\r
2080 codeHighLighter = new Test.CodeHighLighter({\r
2081 source: block.func.toString()\r
2082 });\r
2083\r
2084 children.push({\r
2085 cls: "sources",\r
2086 html: codeHighLighter.renderJsSources()\r
2087 });\r
2088 }\r
2089 }\r
2090 \r
2091 this.el = new jasmine.Dom({\r
2092 cls: cls, \r
2093 children: children\r
2094 });\r
2095\r
2096 return this;\r
2097};\r
2098\r
2099Test.panel.Blocks.prototype.remove = function() {\r
2100 this.el.parentNode.removeChild(this.el);\r
2101};/**\r
2102 * Renders spec dom sandbox tool.\r
2103 * @param {Jasmine.spec} spec The spec.\r
2104 * @param {HTMLElement} panelsEl The HTMLElement which encapsulate the tools panels.\r
2105 */\r
2106Test.panel.Sandbox = function(config) {\r
2107 this.persist = true;\r
2108\r
2109 this.render();\r
2110\r
2111 return this;\r
2112};\r
2113\r
2114/**\r
2115 * Renders spec dom sandbox innerHTML.\r
2116 * @return {HTMElement} The formatted dom sandbox innerHTML.\r
2117 */\r
2118Test.panel.Sandbox.prototype.render = function() {\r
2119 this.el = new jasmine.Dom({\r
2120 cls: "panel sandbox hideMe"\r
2121 });\r
2122};/**\r
2123 * Renders infos panel.\r
2124 */\r
2125Test.panel.Infos = function() {\r
2126 this.el = new jasmine.Dom({\r
2127 tag: "div",\r
2128 cls: "panel infos",\r
2129 children: [{\r
2130 cls: "logs"\r
2131 }]\r
2132 });\r
2133 this.logs = this.el.childNodes[0];\r
2134 this.persist = true;\r
2135 return this;\r
2136};\r
2137\r
2138/**\r
2139 * Print a message into console.\r
2140 * @param {String} message The message.\r
2141 * @param {String} cls (optional) an extra cls to add to the message.\r
2142 */\r
2143Test.panel.Infos.prototype.log = function(message, cls) {\r
2144 var log = this.logs.appendChild(new jasmine.Dom({\r
2145 cls: "infoMessage",\r
2146 html: message\r
2147 }));\r
2148 \r
2149 if (cls) {\r
2150 jasmine.Dom.addCls(log, cls);\r
2151 }\r
2152};/**\r
2153 * @class jasmine.panel.jsCoverage\r
2154 * Creates and renders a per spec jscoverage panel.\r
2155 * @param {Object} config The configuration object.\r
2156 */\r
2157Test.panel.jsCoverage = function(config) {\r
2158 this.el = new jasmine.Dom({\r
2159 tag: "div",\r
2160 cls: "panel jsCoverage",\r
2161 children: [{\r
2162 cls: "sources",\r
2163 html: new Test.CodeHighLighter({\r
2164 source: config.suite.jscoverage.file.source.join("\n"),\r
2165 linesFromJsCoverage: config.suite.jscoverage.file,\r
2166 highLightCode: false\r
2167 }).renderJsSources()\r
2168 }]\r
2169 });\r
2170 return this; \r
2171};\r
2172\r
2173Test.panel.jsCoverage.prototype.remove = function() {\r
2174 this.el.parentNode.removeChild(this.el);\r
2175};/**\r
2176 * @class jasmine.panel.jsCoverageSummary\r
2177 * Creates and renders the persistant jscoverage summary panel.\r
2178 * @param {Object} config The configuration object.\r
2179 */\r
2180Test.panel.jsCoverageSummary = function(config) {\r
2181 var me = this;\r
2182 \r
2183 me.el = new jasmine.Dom({\r
2184 tag: "div",\r
2185 cls: "panel jsCoverageSummary hideMe",\r
2186 onclick: function() {\r
2187 me.onClick.apply(me, arguments);\r
2188 },\r
2189 children: [{\r
2190 cls: "sbody"\r
2191 }]\r
2192 });\r
2193 \r
2194 me.body = me.el.childNodes[0];\r
2195 me.persist = true;\r
2196 this.renderSummary();\r
2197 return me; \r
2198};\r
2199\r
2200/**\r
2201 * Renders summary view.\r
2202 */\r
2203Test.panel.jsCoverageSummary.prototype.renderSummary = function() {\r
2204 var coverage = Test.jsCoverage.getCoverage(),\r
2205 filename, result;\r
2206 \r
2207 if (!this.summary) {\r
2208 result = '<table class="summary" border="0" cellpadding="0" cellspacing="0"><tbody>';\r
2209 result += '<tr class="line header"><td class="fileName">File</td><td class="statements">Statements</td><td class="executed">Executed</td><td class="percentage">Percentage</td></tr>'; \r
2210 result += '<tr class="line total">';\r
2211 result += '<td class="fileName">Total</td>';\r
2212 result += '<td class="statements">' + Test.jsCoverage.statements + "</td>";\r
2213 result += '<td class="executed">' + Test.jsCoverage.executed + "</td>";\r
2214 result += '<td class="percentage">' + this.renderPercentage(Test.jsCoverage.percentage) + "</td>";\r
2215 result += '</tr>';\r
2216 \r
2217 for (filename in coverage) {\r
2218 if (!coverage.hasOwnProperty(filename)) {\r
2219 continue;\r
2220 }\r
2221 result += '<tr class="line">';\r
2222 result += '<td class="fileName"><a>' + filename + "</a></td>";\r
2223 result += '<td class="statements">' + coverage[filename].statements + "</td>";\r
2224 result += '<td class="executed">' + coverage[filename].executed + "</td>";\r
2225 result += '<td class="percentage">' + this.renderPercentage(coverage[filename].percentage) + "</td>";\r
2226 result += '</tr>';\r
2227 }\r
2228 result += '</tbody></table>';\r
2229 this.summary = result;\r
2230 }\r
2231 this.body.innerHTML = this.summary;\r
2232};\r
2233\r
2234/**\r
2235 * Renders percentage progress bar.\r
2236 * @return {String} The progressbar html.\r
2237 */\r
2238Test.panel.jsCoverageSummary.prototype.renderPercentage = function(percent) {\r
2239 var result = percent + '%<div class="limit" style="width:300px;">';\r
2240 result += '<div class="result" style="width:' + 3 * percent + 'px;"></div>';\r
2241 \r
2242 result += '</div>';\r
2243 return result;\r
2244};\r
2245\r
2246/**\r
2247 * Renders percentage progress bar.\r
2248 * @return {String} The progressbar html.\r
2249 */\r
2250Test.panel.jsCoverageSummary.prototype.onClick = function(event) {\r
2251 var el;\r
2252 event = event || window.event;\r
2253 el = event.target || event.srcElement;\r
2254\r
2255 if (el.tagName === "A") {\r
2256 this.renderSource(Test.jsCoverage.getCoverage()[el.innerHTML]);\r
2257 }\r
2258 \r
2259 if (jasmine.Dom.hasCls(el,"back")) {\r
2260 this.renderSummary();\r
2261 }\r
2262};\r
2263\r
2264/**\r
2265 * Renders file source.\r
2266 */\r
2267Test.panel.jsCoverageSummary.prototype.renderSource = function(coverage) {\r
2268 this.body.innerHTML = "";\r
2269 this.body.appendChild(new jasmine.Dom({\r
2270 cls: "back",\r
2271 html: "Back"\r
2272 }));\r
2273 \r
2274 this.body.appendChild(new jasmine.Dom({\r
2275 cls: "sources",\r
2276 html: new Test.CodeHighLighter({\r
2277 source: coverage.source.join("\n"),\r
2278 linesFromJsCoverage: coverage,\r
2279 highLightCode: false\r
2280 }).renderJsSources()\r
2281 }));\r
2282};/**\r
2283 * Renders stack trace tool.\r
2284 * @param {Jasmine.spec} The jasmine spec.\r
2285 * @return {HTMLElement} The created HTMLElement.\r
2286 */\r
2287Test.panel.StackTrace = function(config) {\r
2288 this.spec = config.spec;\r
2289 this.badLinesEls = [];\r
2290 \r
2291 var resultItems = this.spec.results().getItems(),\r
2292 length = resultItems.length,\r
2293 result,\r
2294 error,\r
2295 lines,\r
2296 i;\r
2297\r
2298 if (jasmine.browser.isIE || !this.spec.hasError) {\r
2299 return this;\r
2300 }\r
2301 \r
2302 for (i = 0; i < length; i++) {\r
2303 result = resultItems[i];\r
2304 if (result.type == "expect" && result.passed && !result.passed()) {\r
2305 if (result.error) {\r
2306 error = result.error;\r
2307 break;\r
2308 }\r
2309 }\r
2310 } \r
2311 \r
2312 if (error) {\r
2313 lines = this.extractStackTrace(error);\r
2314\r
2315 this.el = new jasmine.Dom({\r
2316 tag: "div",\r
2317 cls: "panel stackTrace",\r
2318 children: this.renderStackLines(lines)\r
2319 });\r
2320 }\r
2321\r
2322 return this;\r
2323};\r
2324\r
2325/**\r
2326 * Extracts error stack trace.\r
2327 * @param {Error} e The javascript error object.\r
2328 * @return {Array} An array which contains all stack trace files and lineNumbers.\r
2329 */\r
2330Test.panel.StackTrace.prototype.extractStackTrace = function(error) {\r
2331 var stack = error.stack || error.stackTrace,\r
2332 results = [],\r
2333 lines, line, length, i, extract, file, lineNumber;\r
2334 \r
2335 if (stack) {\r
2336 lines = stack.split("\n");\r
2337 length = lines.length;\r
2338 for(i = 0; i < length; i++) {\r
2339 line = lines[i];\r
2340 if (line.search("jasmine.js") === -1) {\r
2341 extract = this.extractFileAndLine(line);\r
2342 if (extract) {\r
2343 results.push(extract);\r
2344 }\r
2345 }\r
2346 }\r
2347 } else {\r
2348 file = error.sourceURL || error.fileName; \r
2349 lineNumber = error.line || error.lineNumber;\r
2350\r
2351 if (file && lineNumber) {\r
2352 results.push({\r
2353 file: file,\r
2354 lineNumber: lineNumber\r
2355 });\r
2356 }\r
2357 }\r
2358 return results;\r
2359};\r
2360\r
2361/**\r
2362 * Extracts filename and line number from a stack trace line.\r
2363 * @param {String} line The stack trace line.\r
2364 * @return {Object} An object containing the filename and the line number or null.\r
2365 */\r
2366Test.panel.StackTrace.prototype.extractRe = /((http:\/\/|file:\/\/\/).*\.js)[^:]*:(\d*)/;\r
2367Test.panel.StackTrace.prototype.extractFileAndLine = function(line) {\r
2368 var result = line.match(this.extractRe);\r
2369\r
2370 if (!result) {\r
2371 return null;\r
2372 }\r
2373\r
2374 return {\r
2375 file: result[1],\r
2376 lineNumber: result[3]\r
2377 }; \r
2378};\r
2379\r
2380/**\r
2381 * Render stack trace lines.\r
2382 * @param {String} file The filename.\r
2383 * @param {String/Number} lineNumber The line number.\r
2384 * @return {Array} An array containing all strace trace HTMLElements.\r
2385 */\r
2386Test.panel.StackTrace.prototype.renderStackLines = function(lines) {\r
2387 var els = [],\r
2388 length = lines.length,\r
2389 el, line, i, file, lineNumber;\r
2390\r
2391 for (i = 0; i < length; i++) {\r
2392 line = lines[i];\r
2393 file = line.file;\r
2394 lineNumber = parseInt(line.lineNumber, 0);\r
2395 el = new jasmine.Dom({\r
2396 cls: "stackTraceLine",\r
2397 children: [{\r
2398 cls: "fileName",\r
2399 html: "File: "+ file + " (line " + lineNumber + ")"\r
2400 },{\r
2401 cls: "sources",\r
2402 html: this.renderTraceFileSource(file, lineNumber) \r
2403 }]\r
2404 });\r
2405 \r
2406 this.badLinesEls.push({\r
2407 el: el.childNodes[1],\r
2408 line: lineNumber\r
2409 });\r
2410 els.push(el);\r
2411 }\r
2412 \r
2413 return els;\r
2414};\r
2415\r
2416/**\r
2417 * Downloads source file.\r
2418 * @param {String} url The filename url.\r
2419 * @return {String} The file source or null.\r
2420 */\r
2421Test.panel.StackTrace.prototype.getFile = function(file) {\r
2422 var request;\r
2423\r
2424 if (jasmine.browser.isIE || Test.Options.remote) {\r
2425 return null;\r
2426 }\r
2427 this.downloadedFiles = this.downloadedFiles || {};\r
2428\r
2429 if (!this.downloadedFiles[file]) {\r
2430 request = new XMLHttpRequest();\r
2431 \r
2432 if (!request) {\r
2433 return null;\r
2434 }\r
2435 request.open("GET", file + "?" + (new Date()).getTime(), false);\r
2436\r
2437 request.send("");\r
2438\r
2439 this.downloadedFiles[file] = request.responseText; \r
2440 }\r
2441 \r
2442 return this.downloadedFiles[file];\r
2443};\r
2444\r
2445/**\r
2446 * Renders stack trace source file.\r
2447 * @param {String} file The filename.\r
2448 * @param {String/Number} lineNumber The line number.\r
2449 * @return {HTMLElement} The javascript source file HTMLElement.\r
2450 */\r
2451Test.panel.StackTrace.prototype.jscoverageFileRe = /(http:\/\/|file:\/\/\/)[^\/]*/;\r
2452\r
2453Test.panel.StackTrace.prototype.renderTraceFileSource = function (file, lineNumber) {\r
2454 var highLightCode = true,\r
2455 source, instrumented_file, i, length, line;\r
2456\r
2457 if (Test.SandBox.getWin()._$jscoverage) {\r
2458 instrumented_file = SandBox.getWin()._$jscoverage[file.replace(this.jscoverageFileRe, "")];\r
2459 if (instrumented_file) {\r
2460 highLightCode = false;\r
2461 source = instrumented_file.source.join("\n");\r
2462 linesFromJsCoverage = {};\r
2463 length = instrumented_file.length;\r
2464 for (i = 0; i < length; i++) {\r
2465 line = instrumented_file[i];\r
2466 if (line === 0) {\r
2467 linesFromJsCoverage[i-1] = true;\r
2468 }\r
2469 }\r
2470 }\r
2471 }\r
2472 source = source || this.getFile(file);\r
2473 \r
2474 return new Test.CodeHighLighter({\r
2475 source: source,\r
2476 highLightCode: highLightCode,\r
2477 lineNumber: lineNumber\r
2478 }).renderJsSources();\r
2479};\r
2480\r
2481/**\r
2482 * Ensure that line which contains the error is visible without scroll.\r
2483 */\r
2484Test.panel.StackTrace.prototype.afterRender = function() {\r
2485 var length = this.badLinesEls.length,\r
2486 badLine, firstChild, el, i, lineHeigth, visiblesLines;\r
2487\r
2488 for (i = 0; i < length; i++) {\r
2489 badLine = this.badLinesEls[i];\r
2490 el = badLine.el;\r
2491 lineHeigth = 16;\r
2492 visiblesLines = el.clientHeight/lineHeigth;\r
2493 el.scrollTop = Math.max(badLine.line - visiblesLines/2, 0) * lineHeigth;\r
2494 }\r
2495 \r
2496 this.badLinesEls = [];\r
2497};\r
2498\r
2499Test.panel.StackTrace.prototype.remove = function() {\r
2500 this.el.parentNode.removeChild(this.el);\r
2501};/**\r
2502 * @class Test.panel.TabPanel\r
2503 * Renders inspection tools htmlElement.\r
2504 * @param {Object} config The configuration object.\r
2505 */\r
2506Test.panel.TabPanel = function(config) {\r
2507 var me = this; \r
2508 \r
2509 me.options = Test.Options.get();\r
2510 \r
2511 me.spec = config.spec;\r
2512 me.container = config.container;\r
2513 me.el = new jasmine.Dom({\r
2514 cls: "tabpanel",\r
2515 onclick: function() {\r
2516 me.onTabPanelClick.apply(me, arguments);\r
2517 },\r
2518 children: [{\r
2519 cls: "toolBar"\r
2520 },{\r
2521 cls: "panels"\r
2522 }]\r
2523 });\r
2524 \r
2525 me.toolbar = me.el.childNodes[0];\r
2526 me.body = me.el.childNodes[1];\r
2527\r
2528 me.children = [];\r
2529 me.tabs = [];\r
2530 \r
2531 \r
2532 me.container.appendChild(me.el);\r
2533 me.renderToolBar();\r
2534 me.add(new Test.panel.Infos({}));\r
2535 me.add(new Test.panel.Sandbox({}));\r
2536 \r
2537 if (me.options.panel) {\r
2538 me.activatePanel(me.options.panel);\r
2539 }\r
2540 \r
2541 return me;\r
2542};\r
2543\r
2544/**\r
2545 * Adds a panel.\r
2546 * @param {Object} panel the panel to be added to this tabPanel.\r
2547 */\r
2548Test.panel.TabPanel.prototype.add = function(panel) {\r
2549 if (panel.el) {\r
2550 this.body.appendChild(panel.el);\r
2551 }\r
2552 if (panel.afterRender) {\r
2553 panel.afterRender();\r
2554 }\r
2555 this.children.push(panel);\r
2556 \r
2557 if (panel.afterRender) {\r
2558 panel.afterRender();\r
2559 }\r
2560};\r
2561\r
2562/**\r
2563 * Adds a tab\r
2564 * @param {Object} panel the panel to be added to this tabPanel.\r
2565 */\r
2566Test.panel.TabPanel.prototype.addTab = function(cls, name, persist) {\r
2567 var el = this.toolbar.appendChild(new jasmine.Dom({\r
2568 tag: "span",\r
2569 cls: "toolbarTab " + cls,\r
2570 html: name\r
2571 }));\r
2572 \r
2573 this.tabs.push({\r
2574 el: el, \r
2575 persist: persist\r
2576 });\r
2577};\r
2578\r
2579/**\r
2580 * Activate a tool panel and render it if needed.\r
2581 * @param {String} cls The panel className.\r
2582 */\r
2583Test.panel.TabPanel.prototype.activatePanel = function(cls) {\r
2584 var children = this.children,\r
2585 length = children.length,\r
2586 rendered = false,\r
2587 child, i;\r
2588 \r
2589 for(i = 0; i < length; i++) {\r
2590 child = children[i].el;\r
2591 jasmine.Dom.addCls(child, "hideMe"); \r
2592 if (jasmine.Dom.hasCls(child, cls)) {\r
2593 jasmine.Dom.removeCls(child, "hideMe");\r
2594 if (children[i].persist && cls !== "jsCoverageSummary") {\r
2595 this.options.panel = cls;\r
2596 } else {\r
2597 delete this.options.panel;\r
2598 }\r
2599 rendered = true;\r
2600 }\r
2601 }\r
2602\r
2603 if (rendered) {\r
2604 return;\r
2605 }\r
2606 \r
2607 if (this.spec) {\r
2608 if (cls === "blocks") {\r
2609 this.add(new Test.panel.Blocks({\r
2610 spec: this.spec\r
2611 }));\r
2612 }\r
2613\r
2614 if (cls === "stackTrace") {\r
2615 this.add(new Test.panel.StackTrace({\r
2616 spec: this.spec\r
2617 })); \r
2618 }\r
2619 }\r
2620 \r
2621 if (this.suite && this.suite.jscoverage) {\r
2622 if (cls === "jsCoverage") {\r
2623 this.add(new Test.panel.jsCoverage({\r
2624 suite: this.suite\r
2625 })); \r
2626 } \r
2627 }\r
2628};\r
2629\r
2630/**\r
2631 * Reporter HTMLElement click dispatcher.\r
2632 * @param {Event} event The event\r
2633 */\r
2634Test.panel.TabPanel.prototype.onTabPanelClick = function(event) {\r
2635 var el;\r
2636 event = event || window.event;\r
2637 el = event.target || event.srcElement;\r
2638\r
2639 if (jasmine.Dom.hasCls(el, "toolbarTab")) {\r
2640 this.onTabClick(el);\r
2641 }\r
2642};\r
2643\r
2644/**\r
2645 * Handle spec tools tab click.\r
2646 * @param {HTMLElement} el The tab HTMLElement.\r
2647 */\r
2648Test.panel.TabPanel.prototype.onTabClick = function(el) {\r
2649 var tools, panels, length, child, i;\r
2650 \r
2651 jasmine.Dom.addCls(el, "selected");\r
2652\r
2653 tools = this.toolbar.childNodes;\r
2654 panels = this.body.childNodes;\r
2655\r
2656 length = tools.length;\r
2657 for(i = 0; i < length; i++) {\r
2658 child = tools[i];\r
2659 if (child != el) { \r
2660 jasmine.Dom.removeCls(child, "selected");\r
2661 }\r
2662 }\r
2663 this.activatePanel(el.className.split(" ")[1]);\r
2664};\r
2665\r
2666\r
2667/**\r
2668 * Renders inspection tabpanel toolbar which contain tabs.\r
2669 * @param {jasmine.Spec} spec The jasmine spec.\r
2670 * @param {HTMLElement} toolBarEl The toolbar HTMLElement\r
2671 */\r
2672Test.panel.TabPanel.prototype.renderToolBar = function() {\r
2673 var spec = this.spec,\r
2674 suite = this.suite,\r
2675 toolbar = this.toolbar;\r
2676 \r
2677 if (this.tabs.length === 0) {\r
2678 this.addTab("infos selected", "Console", true);\r
2679 this.addTab("sandbox", "Iframe", true);\r
2680 } else {\r
2681 jasmine.Dom.addCls(this.tabs[0].el, "selected");\r
2682 }\r
2683 \r
2684 if (spec) {\r
2685 this.addTab("blocks", "Blocks");\r
2686 \r
2687 if (!jasmine.browser.isIE && !jasmine.browser.isOpera && this.spec.hasError) {\r
2688 this.addTab("stackTrace", "Stack Trace");\r
2689 }\r
2690 }\r
2691 \r
2692 if (suite && suite.jscoverage) {\r
2693 this.addTab("jsCoverage", "Suite Coverage"); \r
2694 }\r
2695};\r
2696\r
2697/**\r
2698 * Removes all non-persistant tabs.\r
2699 */\r
2700Test.panel.TabPanel.prototype.resetToolBar = function() {\r
2701 var children = this.tabs,\r
2702 length = children.length, \r
2703 child, i;\r
2704\r
2705 for (i = length - 1; i >= 0; i--) {\r
2706 child = children[i];\r
2707 if (!child.persist) {\r
2708 this.toolbar.removeChild(child.el);\r
2709 jasmine.array.remove(children, child);\r
2710 }\r
2711 jasmine.Dom.removeCls(child.el, "selected");\r
2712 }\r
2713 \r
2714 this.renderToolBar();\r
2715};\r
2716\r
2717/**\r
2718 * Removes all non-persistant panels.\r
2719 */\r
2720Test.panel.TabPanel.prototype.resetPanels = function() {\r
2721 var children = this.children,\r
2722 length = children.length, \r
2723 child, i;\r
2724\r
2725 for (i = length - 1; i >= 0; i--) {\r
2726 child = children[i];\r
2727 if (!child.persist) {\r
2728 child.remove();\r
2729 jasmine.array.remove(children, child);\r
2730 }\r
2731 jasmine.Dom.addCls(child.el, "hideMe");\r
2732 }\r
2733 \r
2734 if (children[0]) {\r
2735 jasmine.Dom.removeCls(children[0].el, "hideMe");\r
2736 }\r
2737};\r
2738\r
2739/**\r
2740 * Sets TabPanel current spec.\r
2741 */\r
2742Test.panel.TabPanel.prototype.setSpec = function(spec) {\r
2743 this.spec = spec;\r
2744 delete this.suite;\r
2745 this.resetToolBar();\r
2746 this.resetPanels();\r
2747};\r
2748\r
2749/**\r
2750 * Sets TabPanel current suite.\r
2751 */\r
2752Test.panel.TabPanel.prototype.setSuite = function(suite) {\r
2753 this.suite = suite;\r
2754 delete this.spec;\r
2755 this.resetToolBar();\r
2756 this.resetPanels();\r
2757};\r
2758\r
2759/**\r
2760 * Resize TabPanel dom element.\r
2761 */\r
2762Test.panel.TabPanel.prototype.resize = function(val) {\r
2763 this.el.style.height = val + "px";\r
2764 this.body.style.height = val - 40 + "px";\r
2765};\r
2766\r
2767/**\r
2768 * Adds jscoverage persistant panel.\r
2769 */\r
2770Test.panel.TabPanel.prototype.addCoverageSummary = function() {\r
2771 this.addTab("jsCoverageSummary", "Coverage Summary", true);\r
2772 this.add(new Test.panel.jsCoverageSummary({}));\r
2773};/**\r
2774 * @class Test.panel.TreeGrid\r
2775 * Creates and renders reporter treegrid.\r
2776 * @param {Object} config The configuration object.\r
2777 */\r
2778Test.panel.TreeGrid = function(config) {\r
2779 var me = this;\r
2780 me.options = Test.Options.get();\r
2781 \r
2782 me.el = document.body.appendChild(new jasmine.Dom({\r
2783 tag: "div",\r
2784 cls: "treegrid",\r
2785 onmousedown: function() {\r
2786 me.onMouseDown.apply(me, arguments);\r
2787 },\r
2788 onmouseup: function() {\r
2789 me.onMouseUp.apply(me, arguments);\r
2790 },\r
2791 onmousemove: function() {\r
2792 me.onMouseMove.apply(me, arguments);\r
2793 },\r
2794 children: [{\r
2795 cls: "header",\r
2796 children: [{\r
2797 cls: "logo",\r
2798 html: "Sencha"\r
2799 },{\r
2800 cls: "statusMessage"\r
2801 },{\r
2802 cls: "toolBar",\r
2803 children: [{\r
2804 tag: "span",\r
2805 cls: "options",\r
2806 children: [\r
2807 Test.Options.renderCheckbox("showPassed", "Show passed"),\r
2808 Test.Options.renderCheckbox("showDisabled", "Show disabled"),\r
2809 Test.Options.renderCheckbox("collapseAll", "Collapse all"),\r
2810 Test.Options.renderCheckbox("disableBodyClean", "Disable Body Autoclean"),\r
2811 Test.Options.renderCheckbox("disableCacheBuster", "Disable CacheBuster"),\r
2812 Test.Options.renderCheckbox("showTimings", "Show Timings"),\r
2813 Test.Options.renderCheckbox("verbose", "Show jasmine logs"),\r
2814 Test.Options.renderCheckbox("autoReload", "Automatic reload"),\r
2815 Test.Options.renderCheckbox("quirksMode", "Quirks Mode")\r
2816 ]\r
2817 },{\r
2818 tag: "a",\r
2819 cls: "actionLink",\r
2820 html: "Run checked",\r
2821 onclick: function() {\r
2822 Test.Options.reloadWindow();\r
2823 }\r
2824 },{\r
2825 tag: "a",\r
2826 cls: "actionLink",\r
2827 html: "Run all",\r
2828 onclick: function() {\r
2829 Test.Options.reloadWindow(true);\r
2830 }\r
2831 }]\r
2832 }]\r
2833 },{\r
2834 tag: "div",\r
2835 cls: "tbody",\r
2836 onclick: function() {\r
2837 me.onBodyClick.apply(me, arguments);\r
2838 }\r
2839 }, {\r
2840 cls: "resizer",\r
2841 html: "......"\r
2842 }]\r
2843 }));\r
2844 me.tabPanel = new Test.panel.TabPanel({\r
2845 container: me.el\r
2846 });\r
2847 \r
2848 Test.Options.check();\r
2849 me.header = me.el.childNodes[0];\r
2850 me.statusMessage = me.header.childNodes[1];\r
2851 me.toolBar = me.header.childNodes[2];\r
2852 me.body = me.el.childNodes[1];\r
2853 me.resizer = me.el.childNodes[2]; \r
2854 \r
2855 me.suites = {};\r
2856 me.specs = {};\r
2857 me.suitesEls = {};\r
2858 me.specsEls = {};\r
2859 if (me.options.resizer) {\r
2860 me.tabPanel.resize(parseInt(me.options.resizer, 10));\r
2861 }\r
2862 me.resizeBody();\r
2863 window.onresize = function() {\r
2864 me.resizeBody();\r
2865 };\r
2866};\r
2867\r
2868/**\r
2869 * Renders suite htmlElement.\r
2870 * @param {jasmine.Suite} suite The jasmine suite.\r
2871 * @return {HTMLElement} The suite HTMLElement\r
2872 */\r
2873Test.panel.TreeGrid.prototype.addSuite = function(suite) {\r
2874 var options = {},\r
2875 parent = suite.parentSuite,\r
2876 padding = 18,\r
2877 prefix = suite.isDisabled() ? "xdescribe :" : "describe: ",\r
2878 cls = "noexpand", \r
2879 row, property;\r
2880 \r
2881 if (suite.children_.length !== 0) {\r
2882 cls = this.options.collapseAll ? "expand" : "collapse";\r
2883 } \r
2884 \r
2885 if (parent) {\r
2886 this.suitesEls[parent.id] || this.addSuite(parent);\r
2887 while(parent) {\r
2888 padding += 18;\r
2889 parent = parent.parentSuite;\r
2890 }\r
2891 }\r
2892 row = this.createRow(this.options.collapseAll && suite.parentSuite, suite);\r
2893 for (property in this.options) {\r
2894 if (!this.options.hasOwnProperty(property)) {\r
2895 continue;\r
2896 }\r
2897 options[property] = this.options[property];\r
2898 }\r
2899\r
2900 options.suite = suite.id;\r
2901 delete options.spec;\r
2902 \r
2903 this.suitesEls[suite.id] = new jasmine.Dom({\r
2904 tag: "div",\r
2905 id: "suite-" + suite.id,\r
2906 cls: "suite " + (suite.isDisabled() ? "disabled" : ""),\r
2907 style: {\r
2908 "paddingLeft": padding + "px"\r
2909 },\r
2910 children: [{\r
2911 cls: cls\r
2912 },{\r
2913 tag: "span",\r
2914 cls: "description",\r
2915 html: prefix + suite.description\r
2916 }]\r
2917 });\r
2918 \r
2919 row.appendChild(this.suitesEls[suite.id]);\r
2920 var clear = new jasmine.Dom({ tag: 'div' });\r
2921 clear.style.clear = 'both';\r
2922 row.appendChild(clear);\r
2923 this.suites[suite.id] = suite;\r
2924 \r
2925 return this.suitesEls[suite.id];\r
2926};\r
2927\r
2928/**\r
2929 * Updates suite dom element by adding a code coverage percentage to it's description.\r
2930 * @param {HTMLElement} The suite dom element.\r
2931 * @param {jasmine.Suite} The jasmine suite.\r
2932 */\r
2933Test.panel.TreeGrid.prototype.updateSuiteEl = function(suite, text) {\r
2934 var description = this.suitesEls[suite.id].childNodes[1];\r
2935 jasmine.Dom.setHTML(description, description.innerHTML + text);\r
2936};\r
2937\r
2938/**\r
2939 * Renders spec htmlElement.\r
2940 * @param {jasmine.Spec} spec The jasmine spec.\r
2941 * @return {HTMLElement} The spec HTMLElement\r
2942 */\r
2943Test.panel.TreeGrid.prototype.addSpec = function(spec) {\r
2944 var options = {},\r
2945 padding = 18,\r
2946 suite = spec.suite,\r
2947 suffix = spec.time ? " (" + spec.time + "s)" : "",\r
2948 row, prefix, status, property, specEl, resultPanel;\r
2949 \r
2950 if (spec.isEnabled()) {\r
2951 prefix = "it ";\r
2952 status = spec.results().passed() ? "passed" : "failed";\r
2953 } else {\r
2954 prefix = "xit ";\r
2955 status = "disabled";\r
2956 }\r
2957 \r
2958 if (suite) {\r
2959 this.suitesEls[suite.id] || this.addSuite(suite);\r
2960 while(suite) {\r
2961 padding += 18;\r
2962 suite = suite.parentSuite;\r
2963 }\r
2964 }\r
2965 \r
2966 row = this.createRow(this.options.collapseAll, spec);\r
2967 for (property in this.options) {\r
2968 if (this.options.hasOwnProperty(property)) {\r
2969 options[property] = this.options[property];\r
2970 }\r
2971 }\r
2972\r
2973 options.spec = spec.id;\r
2974 delete options.suite;\r
2975 \r
2976 specEl = {\r
2977 id: "spec-" + spec.id,\r
2978 cls: "spec " + status,\r
2979 style: {\r
2980 "paddingLeft": padding + "px"\r
2981 },\r
2982 children: [{\r
2983 cls: this.options.collapseAll ? "expand" : "collapse"\r
2984 },{\r
2985 tag: "span",\r
2986 cls: "description",\r
2987 html: prefix + spec.description + suffix\r
2988 }]\r
2989 };\r
2990\r
2991 resultPanel = this.renderSpecResults(spec);\r
2992 if (this.options.collapseAll) {\r
2993 resultPanel.style.display = "none";\r
2994 }\r
2995 \r
2996 if (resultPanel.innerHTML === "") {\r
2997 specEl.children[0].cls = "noexpand";\r
2998 }\r
2999 \r
3000 specEl.children.push(resultPanel);\r
3001 \r
3002 specEl = new jasmine.Dom(specEl);\r
3003 this.specsEls[spec.id] = specEl;\r
3004 this.specs[spec.id] = spec;\r
3005 row.appendChild(specEl);\r
3006 jasmine.Dom.addCls(row, status);\r
3007 var clear = new jasmine.Dom({ tag: 'div' });\r
3008 clear.style.clear = 'both';\r
3009 row.appendChild(clear);\r
3010};\r
3011\r
3012/**\r
3013 * Returns a suite by id.\r
3014 * @param {String/Number} id The suite id.\r
3015 * @return {jasmine.Suite} The jasmine suite.\r
3016 */\r
3017Test.panel.TreeGrid.prototype.getSuite = function(id) {\r
3018 return this.suites[parseInt(id, 10)];\r
3019};\r
3020\r
3021/**\r
3022 * Returns a spec by id.\r
3023 * @param {String/Number} id The spec id.\r
3024 * @return {jasmine.Spec} The jasmine spec.\r
3025 */\r
3026Test.panel.TreeGrid.prototype.getSpec = function(id) {\r
3027 return this.specs[parseInt(id, 10)];\r
3028};\r
3029\r
3030/**\r
3031 * Body elements click event dispatcher.\r
3032 */\r
3033Test.panel.TreeGrid.prototype.onBodyClick = function(event) {\r
3034 event = event || window.event;\r
3035 var el = event.target || event.srcElement,\r
3036 cls = el.className,\r
3037 i;\r
3038 \r
3039 if (cls) {\r
3040 if (jasmine.Dom.hasCls(el, "collapse")) {\r
3041 this.onCollapse(el);\r
3042 return;\r
3043 }\r
3044\r
3045 if (jasmine.Dom.hasCls(el, "expand")) {\r
3046 this.onExpand(el);\r
3047 return;\r
3048 }\r
3049 if (jasmine.Dom.hasCls(el, "select-checkbox")) {\r
3050 this.onCheck(el);\r
3051 return;\r
3052 }\r
3053 for (i = 0; i < 6; i++) {\r
3054 if (cls && jasmine.Dom.hasCls(el, "row")) {\r
3055 this.onRowClick(el);\r
3056 return;\r
3057 }\r
3058 el = el.parentNode;\r
3059 if (!el) {\r
3060 break;\r
3061 }\r
3062 cls = el.className;\r
3063 }\r
3064 }\r
3065};\r
3066\r
3067/**\r
3068 * Checkboxes listener.\r
3069 */\r
3070Test.panel.TreeGrid.prototype.onCheck = function(el) {\r
3071 var next = el.parentNode.nextSibling,\r
3072 id;\r
3073\r
3074 if (jasmine.Dom.hasCls(next,"spec")) {\r
3075 id = parseInt(next.id.replace("spec-", ""), 10);\r
3076 if (el.checked) {\r
3077 if (jasmine.array.indexOf(this.options.specs, id) === -1) {\r
3078 this.options.specs.push(id);\r
3079 }\r
3080 } else {\r
3081 jasmine.array.remove(this.options.specs, id);\r
3082 }\r
3083 } else {\r
3084 id = parseInt(next.id.replace("suite-", ""), 10);\r
3085 if (el.checked) {\r
3086 if (jasmine.array.indexOf(this.options.suites, id) === -1) {\r
3087 this.options.suites.push(id);\r
3088 }\r
3089 } else {\r
3090 jasmine.array.remove(this.options.suites, id);\r
3091 }\r
3092 }\r
3093};\r
3094\r
3095/**\r
3096 * Returns row dom element by spec or suite.\r
3097 * @param {jasmine.Suite/jasmine.Spec} o A suite or a spec.\r
3098 * @return {HTMLElement} The row dom element.\r
3099 */\r
3100Test.panel.TreeGrid.prototype.getRow = function(o) {\r
3101 if (!o.suite && this.suitesEls[o.id]) {\r
3102 return this.suitesEls[o.id].parentNode;\r
3103 } else if (this.specsEls[o.id]) {\r
3104 return this.specsEls[o.id].parentNode;\r
3105 }\r
3106};\r
3107\r
3108/**\r
3109 * Iterates nested rows calling the supplied function.\r
3110 * @param {HTMLElement} row The row.\r
3111 * @param {Function} fn The function.\r
3112 * @param {Boolean} recursive recurse in all children suite (default to true)\r
3113 */\r
3114Test.panel.TreeGrid.prototype.onEachRow = function(row, fn, recursive) {\r
3115 var me = this,\r
3116 id = row.childNodes[1].id, \r
3117 traverse = function(s, func) {\r
3118 var children = s.children_,\r
3119 i, child, length, r;\r
3120 \r
3121 if (children) {\r
3122 length = children.length;\r
3123 for (i = 0; i < length; i++) {\r
3124 child = children[i];\r
3125 r = me.getRow(child);\r
3126 if (r) {\r
3127 func.call(me, r, child);\r
3128 if (child.children_ && recursive !== false) {\r
3129 traverse(child, func);\r
3130 }\r
3131 }\r
3132 }\r
3133 }\r
3134 },\r
3135 spec, suite;\r
3136 \r
3137 if (id.search("suite") !== -1) {\r
3138 suite = this.getSuite(id.replace("suite-", ""));\r
3139 traverse(suite, fn);\r
3140 } else {\r
3141 spec = this.getSpec(id.replace("spec-", ""));\r
3142 traverse(spec, fn);\r
3143 }\r
3144};\r
3145\r
3146/**\r
3147 * Collapse click handler.\r
3148 */\r
3149Test.panel.TreeGrid.prototype.onCollapse = function(el) {\r
3150 el = el.parentNode;\r
3151 jasmine.Dom.setCls(el.childNodes[0], "expand");\r
3152 \r
3153 if (jasmine.Dom.hasCls(el, "suite")) {\r
3154 this.onEachRow(el.parentNode, function(row, o) {\r
3155 var childNode = row.childNodes[1],\r
3156 icon = childNode.childNodes[0],\r
3157 content = childNode.childNodes[2];\r
3158 \r
3159 row.style.display = "none";\r
3160 if (jasmine.Dom.hasCls(icon, "collapse")) {\r
3161 jasmine.Dom.setCls(icon, "expand");\r
3162 }\r
3163 if (o.suite) {\r
3164 content.style.display = "none";\r
3165 }\r
3166 });\r
3167 } else {\r
3168 el.childNodes[2].style.display = "none";\r
3169 }\r
3170};\r
3171\r
3172/**\r
3173 * Expand click handler.\r
3174 */\r
3175Test.panel.TreeGrid.prototype.onExpand = function(el) {\r
3176 el = el.parentNode;\r
3177 jasmine.Dom.setCls(el.childNodes[0], "collapse");\r
3178 \r
3179 if (jasmine.Dom.hasCls(el, "suite")) {\r
3180 this.onEachRow(el.parentNode, function(row, o) {\r
3181 row.style.display = "block";\r
3182 }, false);\r
3183 } else {\r
3184 el.childNodes[2].style.display = "block";\r
3185 }\r
3186};\r
3187\r
3188/**\r
3189 * Row click click handler.\r
3190 */\r
3191Test.panel.TreeGrid.prototype.onRowClick = function(el) {\r
3192 var rows = el.parentNode.childNodes,\r
3193 length = rows.length, \r
3194 id, i;\r
3195 \r
3196 for (i = 0; i < length; i++) {\r
3197 jasmine.Dom.removeCls(rows[i], "selected");\r
3198 }\r
3199 \r
3200 jasmine.Dom.addCls(el, "row selected");\r
3201 id = el.childNodes[1].id;\r
3202 \r
3203 if (id.search("spec") !== -1) {\r
3204 this.tabPanel.setSpec(this.getSpec(id.replace("spec-", "")));\r
3205 }\r
3206 if (id.search("suite") !== -1) {\r
3207 this.tabPanel.setSuite(this.getSuite(id.replace("suite-", "")));\r
3208 }\r
3209};\r
3210\r
3211/**\r
3212 * Creates row dom element.\r
3213 * @param {Boolean} hide Sets the row visibility.\r
3214 * @param {jasmine.Suite/jasmine.Spec} The suite or the spec.\r
3215 * @return {HTMLElement} The row.\r
3216 */\r
3217Test.panel.TreeGrid.prototype.createRow = function(hide, o) {\r
3218 var row = this.body.appendChild(new jasmine.Dom({\r
3219 tag: "div",\r
3220 cls: "row",\r
3221 style: {\r
3222 display: hide ? "none" : "block" \r
3223 },\r
3224 children: [{\r
3225 cls: "checkbox-col",\r
3226 children: [{\r
3227 tag: "input",\r
3228 cls: "select-checkbox",\r
3229 type: "checkbox"\r
3230 }]\r
3231 }]\r
3232\r
3233 }));\r
3234 \r
3235 if (Test.Options.isChecked(o)) {\r
3236 row.childNodes[0].childNodes[0].checked = true;\r
3237 }\r
3238 \r
3239 return row;\r
3240};\r
3241\r
3242/**\r
3243 * Resizer\r
3244 */\r
3245 \r
3246/**\r
3247 * MouseDown event listener. (resizing starts)\r
3248 */\r
3249Test.panel.TreeGrid.prototype.onMouseDown = function(event) {\r
3250 var el;\r
3251 \r
3252 event = event || window.event;\r
3253 el = event.target || event.srcElement;\r
3254\r
3255 if (jasmine.Dom.hasCls(el, "resizer")) {\r
3256 if (event.preventDefault) {\r
3257 event.preventDefault();\r
3258 } else {\r
3259 event.returnValue = false;\r
3260 }\r
3261 \r
3262 this.pageY = event.pageY || event.clientY;\r
3263\r
3264 this.startHeight = this.tabPanel.el.clientHeight;\r
3265 document.body.style.cursor = "row-resize";\r
3266 }\r
3267};\r
3268\r
3269/**\r
3270 * MouseDown event listener. (resize in progress)\r
3271 */\r
3272Test.panel.TreeGrid.prototype.onMouseMove = function(event) {\r
3273 var el, diff;\r
3274 if (this.pageY) {\r
3275 event = event || window.event;\r
3276 el = event.target || event.srcElement;\r
3277 diff = Math.max(200, this.startHeight - ((event.pageY || event.clientY)- this.pageY));\r
3278 diff = Math.min(diff, document.body.clientHeight - 200);\r
3279 \r
3280 this.tabPanel.resize(diff);\r
3281 this.options.resizer = diff;\r
3282 this.resizeBody();\r
3283 }\r
3284};\r
3285\r
3286/**\r
3287 * MouseUp event listener. (resize ends)\r
3288 */\r
3289Test.panel.TreeGrid.prototype.onMouseUp = function(event) {\r
3290 document.body.style.cursor = "auto";\r
3291 delete this.pageY;\r
3292};\r
3293\r
3294\r
3295/**\r
3296 * Returns treegrid innerHeight.\r
3297 * @return {Number} The innerHeight.\r
3298 */\r
3299Test.panel.TreeGrid.prototype.getInnerHeight = function() {\r
3300 return (window.innerHeight || document.documentElement.clientHeight) - this.header.offsetTop * 2;\r
3301};\r
3302\r
3303/**\r
3304 * Resizes treegrid.\r
3305 */\r
3306Test.panel.TreeGrid.prototype.resizeBody = function() {\r
3307 var height = this.getInnerHeight();\r
3308 \r
3309 height -= this.resizer.offsetHeight + this.tabPanel.el.offsetHeight + this.header.offsetHeight;\r
3310 height -= 2;\r
3311 height = Math.max(30, height);\r
3312 this.body.style.height = height + 'px';\r
3313};\r
3314\r
3315/**\r
3316 * End of Resizer\r
3317 */\r
3318 \r
3319/**\r
3320 * Renders specs results.\r
3321 * @param {jasmine.Spec} spec The spec.\r
3322 * @return {HTMLElement} The spec results dom element.\r
3323 */\r
3324Test.panel.TreeGrid.prototype.renderSpecResults = function(spec) {\r
3325 var resultItems = spec.results().getItems(),\r
3326 length = resultItems.length,\r
3327 resultsEl,\r
3328 resultEl,\r
3329 result,\r
3330 i;\r
3331 \r
3332 resultsEl = new jasmine.Dom({\r
3333 cls: "results" \r
3334 });\r
3335 \r
3336 for (i = 0; i < length; i++) {\r
3337 result = resultItems[i];\r
3338 if (result.type === "expect" && result.passed) {\r
3339 \r
3340 if (!result.passed()) {\r
3341 resultEl = this.renderFailedResult(result);\r
3342 } else {\r
3343 resultEl = this.renderPassedResult(result);\r
3344 }\r
3345 \r
3346 if (i === 0) {\r
3347 jasmine.Dom.addCls(resultEl, "first");\r
3348 }\r
3349 \r
3350 resultsEl.appendChild(resultEl);\r
3351 \r
3352 if (result.error) {\r
3353 break;\r
3354 }\r
3355 }\r
3356 }\r
3357\r
3358 return resultsEl;\r
3359};\r
3360\r
3361/**\r
3362 * Renders failed spec result.\r
3363 * @param {Object} result The spec result.\r
3364 * @return {HTMLElement} The spec result message HTMLElement\r
3365 */\r
3366Test.panel.TreeGrid.prototype.renderFailedResult = function(result) {\r
3367 var message = result.message,\r
3368 children;\r
3369\r
3370 children = [{\r
3371 cls: "prettyPrint",\r
3372 html: jasmine.util.htmlEscape(message)\r
3373 }];\r
3374 \r
3375 return new jasmine.Dom({\r
3376 cls: "resultMessage fail",\r
3377 children: children\r
3378 });\r
3379};\r
3380\r
3381\r
3382/**\r
3383 * Renders failed spec result.\r
3384 * @param {Object} result The spec result.\r
3385 * @return {HTMLElement} The spec result message HTMLElement\r
3386 */\r
3387Test.panel.TreeGrid.prototype.renderPassedResult = function(result) {\r
3388 var children = [{\r
3389 cls: "prettyPrint",\r
3390 html: "Actual: " + jasmine.pp(result.actual) + "\nExpected: " + jasmine.pp(result.expected) + "\nMatcher: " + result.matcherName + "."\r
3391 }];\r
3392 \r
3393 return new jasmine.Dom({\r
3394 cls: "resultMessage pass",\r
3395 children: children\r
3396 });\r
3397};\r
3398\r
3399/**\r
3400 * Returns tabPanel console.\r
3401 */\r
3402Test.panel.TreeGrid.prototype.getInfoPanel = function() {\r
3403 return this.tabPanel.children[0];\r
3404};\r
3405\r
3406/**\r
3407 * Print a message into info console.\r
3408 * @param {String} message The message.\r
3409 * @param {String} cls (optional) an extra cls to add to the message.\r
3410 */\r
3411Test.panel.TreeGrid.prototype.log = function(message, cls) {\r
3412 this.getInfoPanel().log(message, cls);\r
3413};\r
3414\r
3415/**\r
3416 * Sets statubar message, this method can also add a className.\r
3417 * @param {String} message The message.\r
3418 * @param {String} cls The className (optional).\r
3419 */ \r
3420Test.panel.TreeGrid.prototype.setStatus = function(message, cls) {\r
3421 jasmine.Dom.setHTML(this.statusMessage, message);\r
3422 if (cls) {\r
3423 jasmine.Dom.addCls(this.statusMessage, cls);\r
3424 }\r
3425};/**\r
3426 * @class Test.Reporter\r
3427 * The Sencha Unit Tests Reporter\r
3428 */\r
3429\r
3430Test.Reporter = function(config) {\r
3431 config = config || {};\r
3432 this.options = Test.Options.get();\r
3433 this.runnedSpecsCount = 0;\r
3434 this.failedSpecsCount = 0;\r
3435 this.disabledSpecsCount = 0;\r
3436 this.optionCheckBoxesEl = {};\r
3437 this.treeGrid = new Test.panel.TreeGrid({});\r
3438 \r
3439};\r
3440\r
3441/**\r
3442 * Called before runner execution.\r
3443 * @param {jasmine.Runner} runner The Jasmine Runner\r
3444 */ \r
3445Test.Reporter.prototype.reportRunnerStarting = function(runner) {\r
3446 this.runner = runner;\r
3447 this.startedAt = new Date();\r
3448 if (Test.BadGlobals) {\r
3449 Test.BadGlobals.setup();\r
3450 }\r
3451 this.logger = this.treeGrid;\r
3452 \r
3453 this.log(">> Started at " + this.startedAt.toString(), "info");\r
3454 \r
3455 if (!this.options.remote) {\r
3456 this.log(">> Warning! Because you access TestReporter locally, stack trace report isn't available.", "warning");\r
3457 }\r
3458\r
3459 this.runner.filter(this.options.suites, this.options.specs);\r
3460\r
3461 if (Test.BadGlobals) {\r
3462 Test.BadGlobals.report(this.logger);\r
3463 }\r
3464};\r
3465\r
3466\r
3467/**\r
3468 * Called after Jasmine runner execution ends.\r
3469 * @param {jasmine.Runner} runner The Jasmine Runner\r
3470 */ \r
3471Test.Reporter.prototype.reportRunnerResults = function(runner) {\r
3472 Test.jsCoverage.updateTotal();\r
3473 this.renderResults(runner);\r
3474};\r
3475\r
3476/**\r
3477 * Called before spec execution.\r
3478 * @param {jasmine.Runner} suite The Jasmine spec\r
3479 */ \r
3480Test.Reporter.prototype.reportSuiteStarting = function(suite) {\r
3481 if (this.options.showTimings) {\r
3482 suite.startedAt = new Date();\r
3483 }\r
3484 if (Test.jsCoverage.isEnabled()) {\r
3485 Test.jsCoverage.add(suite);\r
3486 }\r
3487};\r
3488/**\r
3489 * Called after suite execution ends.\r
3490 * @param {jasmine.Runner} suite A Jasmine suite\r
3491 */ \r
3492Test.Reporter.prototype.reportSuiteResults = function(suite) {\r
3493 var suiteEl = this.treeGrid ? this.treeGrid.suitesEls[suite.id] : undefined,\r
3494 status;\r
3495\r
3496 if (suite.isEnabled()) {\r
3497 if (this.options.showTimings) {\r
3498 suite.time = (((new Date()).getTime() - suite.startedAt.getTime())/ 1000).toFixed(3);\r
3499 }\r
3500 \r
3501 Test.jsCoverage.update(suite);\r
3502 \r
3503 if (!suite.parentSuite && Test.BadGlobals) {\r
3504 Test.BadGlobals.report(this.logger, suite);\r
3505 }\r
3506 \r
3507 if (this.treeGrid && this.options.showPassed && !suiteEl) {\r
3508 suiteEl = this.treeGrid.addSuite(suite);\r
3509 }\r
3510 \r
3511 if (suiteEl) {\r
3512 status = suite.results().passed() ? "passed" : "failed";\r
3513 jasmine.Dom.addCls(suiteEl, status);\r
3514 jasmine.Dom.addCls(suiteEl.parentNode, status);\r
3515\r
3516 if (Test.jsCoverage.isEnabled()) {\r
3517 this.treeGrid.updateSuiteEl(suite, Test.jsCoverage.getSuiteCoverage(suite));\r
3518 }\r
3519 \r
3520 if (suite.time) {\r
3521 this.treeGrid.updateSuiteEl(suite, " (" + suite.time + "s)");\r
3522 }\r
3523 }\r
3524 \r
3525 } else if (this.treeGrid && this.options.showDisabled && !suiteEl) {\r
3526 this.treeGrid.addSuite(suite);\r
3527 }\r
3528 \r
3529};\r
3530\r
3531/**\r
3532 * Called before spec execution.\r
3533 * @param {jasmine.Runner} suite The Jasmine spec\r
3534 */ \r
3535Test.Reporter.prototype.reportSpecStarting = function(spec) {\r
3536 this.currentSpec = spec;\r
3537\r
3538 if (spec.isEnabled()) {\r
3539 if (this.options.showTimings) {\r
3540 spec.startedAt = new Date();\r
3541 }\r
3542 this.treeGrid.setStatus("Running: " + jasmine.util.htmlEscape(spec.getFullName()));\r
3543 }\r
3544};\r
3545\r
3546/**\r
3547 * Called after spec execution.\r
3548 * @param {jasmine.Runner} suite The Jasmine spec\r
3549 */ \r
3550Test.Reporter.prototype.reportSpecResults = function(spec) {\r
3551 var results, status;\r
3552\r
3553 if (spec.isEnabled()) {\r
3554 if (this.options.showTimings) {\r
3555 spec.time = (((new Date()).getTime() - spec.startedAt.getTime())/ 1000).toFixed(3);\r
3556 }\r
3557 results = spec.results();\r
3558 status = results.passed() ? "passed" : "failed";\r
3559\r
3560 if(status === "failed") {\r
3561 this.failedSpecsCount = this.failedSpecsCount + 1;\r
3562 }\r
3563 \r
3564 if ((status === "failed" || this.options.showPassed) && spec.isEnabled() && this.treeGrid) {\r
3565 this.treeGrid.addSpec(spec);\r
3566 }\r
3567\r
3568 Test.SandBox.save(spec);\r
3569 \r
3570\r
3571 this.runnedSpecsCount = this.runnedSpecsCount + 1;\r
3572 } else {\r
3573 this.disabledSpecsCount = this.disabledSpecsCount + 1;\r
3574 if (this.treeGrid && this.options.showDisabled) {\r
3575 this.treeGrid.addSpec(spec);\r
3576 }\r
3577 }\r
3578};\r
3579\r
3580/**\r
3581 * Updates runner message with failed and passed specs\r
3582 * @param {jasmine.Runner} runner The jasmine runner.\r
3583 */\r
3584Test.Reporter.prototype.renderResults = function(runner) {\r
3585 var cls = (this.failedSpecsCount > 0) ? "failed" : "passed",\r
3586 runTime,\r
3587 message;\r
3588 \r
3589 runTime = (new Date().getTime() - this.startedAt.getTime()) / 1000;\r
3590\r
3591 message = this.runnedSpecsCount + " spec" +\r
3592 (this.runnedSpecsCount === 1 ? "" : "s" ) + " ran, " +\r
3593 this.failedSpecsCount + " failure" +\r
3594 (this.failedSpecsCount === 1 ? "" : "s") +\r
3595 " and " + this.disabledSpecsCount + " disabled";\r
3596 \r
3597 message += " in " + runTime + "s";\r
3598 \r
3599 message += Test.jsCoverage.getTotal() + ".";\r
3600 \r
3601 if (this.treeGrid) {\r
3602 if (Test.SandBox.getWin()._$jscoverage) {\r
3603 this.treeGrid.tabPanel.addCoverageSummary();\r
3604 }\r
3605 this.treeGrid.setStatus(message, cls);\r
3606 }\r
3607 this.log(">> Finished at " + new Date().toString(), "info");\r
3608\r
3609};\r
3610\r
3611Test.Reporter.prototype.log = function() { \r
3612 if (this.options.verbose || arguments.length === 2) {\r
3613 this.logger.log.apply(this.logger, arguments);\r
3614 }\r
3615};\r
3616\r
3617Test.Reporter.prototype.getIframeContainer = function() {\r
3618 if (this.treeGrid) {\r
3619 return this.treeGrid.tabPanel.children[1].el;\r
3620 }\r
3621 return document.body;\r
3622};\r