Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-yuitest', function(Y) {
2
    var YAHOO    = Y.YUI2;
3
    /*
4
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
5
Code licensed under the BSD License:
6
http://developer.yahoo.com/yui/license.html
7
version: 2.9.0
8
*/
9
YAHOO.namespace("tool");
10
 
11
//-----------------------------------------------------------------------------
12
// TestCase object
13
//-----------------------------------------------------------------------------
14
(function(){
15
 
16
    //used for autogenerating test case names
17
    var tempId = 0;
18
 
19
    /**
20
     * Test case containing various tests to run.
21
     * @param template An object containing any number of test methods, other methods,
22
     *                 an optional name, and anything else the test case needs.
23
     * @class TestCase
24
     * @namespace YAHOO.tool
25
     * @constructor
26
     */
27
    YAHOO.tool.TestCase = function (template /*:Object*/) {
28
 
29
        /**
30
         * Special rules for the test case. Possible subobjects
31
         * are fail, for tests that should fail, and error, for
32
         * tests that should throw an error.
33
         */
34
        this._should /*:Object*/ = {};
35
 
36
        //copy over all properties from the template to this object
37
        for (var prop in template) {
38
            this[prop] = template[prop];
39
        }
40
 
41
        //check for a valid name
42
        if (!YAHOO.lang.isString(this.name)){
43
            /**
44
             * Name for the test case.
45
             */
46
            this.name /*:String*/ = "testCase" + (tempId++);
47
        }
48
 
49
    };
50
 
51
 
52
    YAHOO.tool.TestCase.prototype = {
53
 
54
        /**
55
         * Resumes a paused test and runs the given function.
56
         * @param {Function} segment (Optional) The function to run.
57
         *      If omitted, the test automatically passes.
58
         * @return {Void}
59
         * @method resume
60
         */
61
        resume : function (segment /*:Function*/) /*:Void*/ {
62
            YAHOO.tool.TestRunner.resume(segment);
63
        },
64
 
65
        /**
66
         * Causes the test case to wait a specified amount of time and then
67
         * continue executing the given code.
68
         * @param {Function} segment (Optional) The function to run after the delay.
69
         *      If omitted, the TestRunner will wait until resume() is called.
70
         * @param {int} delay (Optional) The number of milliseconds to wait before running
71
         *      the function. If omitted, defaults to zero.
72
         * @return {Void}
73
         * @method wait
74
         */
75
        wait : function (segment /*:Function*/, delay /*:int*/) /*:Void*/{
76
            var args = arguments;
77
            if (YAHOO.lang.isFunction(args[0])){
78
                throw new YAHOO.tool.TestCase.Wait(args[0], args[1]);
79
            } else {
80
                throw new YAHOO.tool.TestCase.Wait(function(){
81
                    YAHOO.util.Assert.fail("Timeout: wait() called but resume() never called.");
82
                }, (YAHOO.lang.isNumber(args[0]) ? args[0] : 10000));
83
            }
84
        },
85
 
86
        //-------------------------------------------------------------------------
87
        // Stub Methods
88
        //-------------------------------------------------------------------------
89
 
90
        /**
91
         * Function to run before each test is executed.
92
         * @return {Void}
93
         * @method setUp
94
         */
95
        setUp : function () /*:Void*/ {
96
        },
97
 
98
        /**
99
         * Function to run after each test is executed.
100
         * @return {Void}
101
         * @method tearDown
102
         */
103
        tearDown: function () /*:Void*/ {
104
        }
105
    };
106
 
107
    /**
108
     * Represents a stoppage in test execution to wait for an amount of time before
109
     * continuing.
110
     * @param {Function} segment A function to run when the wait is over.
111
     * @param {int} delay The number of milliseconds to wait before running the code.
112
     * @class Wait
113
     * @namespace YAHOO.tool.TestCase
114
     * @constructor
115
     *
116
     */
117
    YAHOO.tool.TestCase.Wait = function (segment /*:Function*/, delay /*:int*/) {
118
 
119
        /**
120
         * The segment of code to run when the wait is over.
121
         * @type Function
122
         * @property segment
123
         */
124
        this.segment /*:Function*/ = (YAHOO.lang.isFunction(segment) ? segment : null);
125
 
126
        /**
127
         * The delay before running the segment of code.
128
         * @type int
129
         * @property delay
130
         */
131
        this.delay /*:int*/ = (YAHOO.lang.isNumber(delay) ? delay : 0);
132
 
133
    };
134
 
135
})();
136
YAHOO.namespace("tool");
137
 
138
 
139
//-----------------------------------------------------------------------------
140
// TestSuite object
141
//-----------------------------------------------------------------------------
142
 
143
/**
144
 * A test suite that can contain a collection of TestCase and TestSuite objects.
145
 * @param {String||Object} data The name of the test suite or an object containing
146
 *      a name property as well as setUp and tearDown methods.
147
 * @namespace YAHOO.tool
148
 * @class TestSuite
149
 * @constructor
150
 */
151
YAHOO.tool.TestSuite = function (data /*:String||Object*/) {
152
 
153
    /**
154
     * The name of the test suite.
155
     * @type String
156
     * @property name
157
     */
158
    this.name /*:String*/ = "";
159
 
160
    /**
161
     * Array of test suites and
162
     * @private
163
     */
164
    this.items /*:Array*/ = [];
165
 
166
    //initialize the properties
167
    if (YAHOO.lang.isString(data)){
168
        this.name = data;
169
    } else if (YAHOO.lang.isObject(data)){
170
        YAHOO.lang.augmentObject(this, data, true);
171
    }
172
 
173
    //double-check name
174
    if (this.name === ""){
175
        this.name = YAHOO.util.Dom.generateId(null, "testSuite");
176
    }
177
 
178
};
179
 
180
YAHOO.tool.TestSuite.prototype = {
181
 
182
    /**
183
     * Adds a test suite or test case to the test suite.
184
     * @param {YAHOO.tool.TestSuite||YAHOO.tool.TestCase} testObject The test suite or test case to add.
185
     * @return {Void}
186
     * @method add
187
     */
188
    add : function (testObject /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
189
        if (testObject instanceof YAHOO.tool.TestSuite || testObject instanceof YAHOO.tool.TestCase) {
190
            this.items.push(testObject);
191
        }
192
    },
193
 
194
    //-------------------------------------------------------------------------
195
    // Stub Methods
196
    //-------------------------------------------------------------------------
197
 
198
    /**
199
     * Function to run before each test is executed.
200
     * @return {Void}
201
     * @method setUp
202
     */
203
    setUp : function () /*:Void*/ {
204
    },
205
 
206
    /**
207
     * Function to run after each test is executed.
208
     * @return {Void}
209
     * @method tearDown
210
     */
211
    tearDown: function () /*:Void*/ {
212
    }
213
 
214
};
215
YAHOO.namespace("tool");
216
 
217
/**
218
 * The YUI test tool
219
 * @module yuitest
220
 * @namespace YAHOO.tool
221
 * @requires yahoo,dom,event,logger
222
 * @optional event-simulate
223
 */
224
 
225
 
226
//-----------------------------------------------------------------------------
227
// TestRunner object
228
//-----------------------------------------------------------------------------
229
 
230
 
231
YAHOO.tool.TestRunner = (function(){
232
 
233
    /**
234
     * A node in the test tree structure. May represent a TestSuite, TestCase, or
235
     * test function.
236
     * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
237
     * @class TestNode
238
     * @constructor
239
     * @private
240
     */
241
    function TestNode(testObject /*:Variant*/){
242
 
243
        /**
244
         * The TestSuite, TestCase, or test function represented by this node.
245
         * @type Variant
246
         * @property testObject
247
         */
248
        this.testObject = testObject;
249
 
250
        /**
251
         * Pointer to this node's first child.
252
         * @type TestNode
253
         * @property firstChild
254
         */
255
        this.firstChild /*:TestNode*/ = null;
256
 
257
        /**
258
         * Pointer to this node's last child.
259
         * @type TestNode
260
         * @property lastChild
261
         */
262
        this.lastChild = null;
263
 
264
        /**
265
         * Pointer to this node's parent.
266
         * @type TestNode
267
         * @property parent
268
         */
269
        this.parent = null;
270
 
271
        /**
272
         * Pointer to this node's next sibling.
273
         * @type TestNode
274
         * @property next
275
         */
276
        this.next = null;
277
 
278
        /**
279
         * Test results for this test object.
280
         * @type object
281
         * @property results
282
         */
283
        this.results /*:Object*/ = {
284
            passed : 0,
285
            failed : 0,
286
            total : 0,
287
            ignored : 0,
288
            duration: 0
289
        };
290
 
291
        //initialize results
292
        if (testObject instanceof YAHOO.tool.TestSuite){
293
            this.results.type = "testsuite";
294
            this.results.name = testObject.name;
295
        } else if (testObject instanceof YAHOO.tool.TestCase){
296
            this.results.type = "testcase";
297
            this.results.name = testObject.name;
298
        }
299
 
300
    }
301
 
302
    TestNode.prototype = {
303
 
304
        /**
305
         * Appends a new test object (TestSuite, TestCase, or test function name) as a child
306
         * of this node.
307
         * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
308
         * @return {Void}
309
         */
310
        appendChild : function (testObject /*:Variant*/) /*:Void*/{
311
            var node = new TestNode(testObject);
312
            if (this.firstChild === null){
313
                this.firstChild = this.lastChild = node;
314
            } else {
315
                this.lastChild.next = node;
316
                this.lastChild = node;
317
            }
318
            node.parent = this;
319
            return node;
320
        }
321
    };
322
 
323
    /**
324
     * Runs test suites and test cases, providing events to allowing for the
325
     * interpretation of test results.
326
     * @namespace YAHOO.tool
327
     * @class TestRunner
328
     * @static
329
     */
330
    function TestRunner(){
331
 
332
        //inherit from EventProvider
333
        TestRunner.superclass.constructor.apply(this,arguments);
334
 
335
        /**
336
         * Suite on which to attach all TestSuites and TestCases to be run.
337
         * @type YAHOO.tool.TestSuite
338
         * @property masterSuite
339
         * @private
340
         * @static
341
         */
342
        this.masterSuite = new YAHOO.tool.TestSuite("yuitests" + (new Date()).getTime());
343
 
344
        /**
345
         * Pointer to the current node in the test tree.
346
         * @type TestNode
347
         * @private
348
         * @property _cur
349
         * @static
350
         */
351
        this._cur = null;
352
 
353
        /**
354
         * Pointer to the root node in the test tree.
355
         * @type TestNode
356
         * @private
357
         * @property _root
358
         * @static
359
         */
360
        this._root = null;
361
 
362
        /**
363
         * Indicates if the TestRunner is currently running tests.
364
         * @type Boolean
365
         * @private
366
         * @property _running
367
         * @static
368
         */
369
        this._running = false;
370
 
371
        /**
372
         * Holds copy of the results object generated when all tests are
373
         * complete.
374
         * @type Object
375
         * @private
376
         * @property _lastResults
377
         * @static
378
         */
379
        this._lastResults = null;
380
 
381
        //create events
382
        var events /*:Array*/ = [
383
            this.TEST_CASE_BEGIN_EVENT,
384
            this.TEST_CASE_COMPLETE_EVENT,
385
            this.TEST_SUITE_BEGIN_EVENT,
386
            this.TEST_SUITE_COMPLETE_EVENT,
387
            this.TEST_PASS_EVENT,
388
            this.TEST_FAIL_EVENT,
389
            this.TEST_IGNORE_EVENT,
390
            this.COMPLETE_EVENT,
391
            this.BEGIN_EVENT
392
        ];
393
        for (var i=0; i < events.length; i++){
394
            this.createEvent(events[i], { scope: this });
395
        }
396
 
397
    }
398
 
399
    YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
400
 
401
        //-------------------------------------------------------------------------
402
        // Constants
403
        //-------------------------------------------------------------------------
404
 
405
        /**
406
         * Fires when a test case is opened but before the first
407
         * test is executed.
408
         * @event testcasebegin
409
         */
410
        TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
411
 
412
        /**
413
         * Fires when all tests in a test case have been executed.
414
         * @event testcasecomplete
415
         */
416
        TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
417
 
418
        /**
419
         * Fires when a test suite is opened but before the first
420
         * test is executed.
421
         * @event testsuitebegin
422
         */
423
        TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
424
 
425
        /**
426
         * Fires when all test cases in a test suite have been
427
         * completed.
428
         * @event testsuitecomplete
429
         */
430
        TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
431
 
432
        /**
433
         * Fires when a test has passed.
434
         * @event pass
435
         */
436
        TEST_PASS_EVENT /*:String*/ : "pass",
437
 
438
        /**
439
         * Fires when a test has failed.
440
         * @event fail
441
         */
442
        TEST_FAIL_EVENT /*:String*/ : "fail",
443
 
444
        /**
445
         * Fires when a test has been ignored.
446
         * @event ignore
447
         */
448
        TEST_IGNORE_EVENT /*:String*/ : "ignore",
449
 
450
        /**
451
         * Fires when all test suites and test cases have been completed.
452
         * @event complete
453
         */
454
        COMPLETE_EVENT /*:String*/ : "complete",
455
 
456
        /**
457
         * Fires when the run() method is called.
458
         * @event begin
459
         */
460
        BEGIN_EVENT /*:String*/ : "begin",
461
 
462
        //-------------------------------------------------------------------------
463
        // Misc Methods
464
        //-------------------------------------------------------------------------
465
 
466
        /**
467
         * Retrieves the name of the current result set.
468
         * @return {String} The name of the result set.
469
         * @method getName
470
         */
471
        getName: function(){
472
            return this.masterSuite.name;
473
        },
474
 
475
        /**
476
         * The name assigned to the master suite of the TestRunner. This is the name
477
         * that is output as the root's name when results are retrieved.
478
         * @param {String} name The name of the result set.
479
         * @return {Void}
480
         * @method setName
481
         */
482
        setName: function(name){
483
            this.masterSuite.name = name;
484
        },
485
 
486
 
487
        //-------------------------------------------------------------------------
488
        // State-Related Methods
489
        //-------------------------------------------------------------------------
490
 
491
        /**
492
         * Indicates that the TestRunner is busy running tests and therefore can't
493
         * be stopped and results cannot be gathered.
494
         * @return {Boolean} True if the TestRunner is running, false if not.
495
         * @method isRunning
496
         */
497
        isRunning: function(){
498
            return this._running;
499
        },
500
 
501
        /**
502
         * Returns the last complete results set from the TestRunner. Null is returned
503
         * if the TestRunner is running or no tests have been run.
504
         * @param {Function} format (Optional) A test format to return the results in.
505
         * @return {Object|String} Either the results object or, if a test format is
506
         *      passed as the argument, a string representing the results in a specific
507
         *      format.
508
         * @method getResults
509
         */
510
        getResults: function(format){
511
            if (!this._running && this._lastResults){
512
                if (YAHOO.lang.isFunction(format)){
513
                    return format(this._lastResults);
514
                } else {
515
                    return this._lastResults;
516
                }
517
            } else {
518
                return null;
519
            }
520
        },
521
 
522
        /**
523
         * Returns the coverage report for the files that have been executed.
524
         * This returns only coverage information for files that have been
525
         * instrumented using YUI Test Coverage and only those that were run
526
         * in the same pass.
527
         * @param {Function} format (Optional) A coverage format to return results in.
528
         * @return {Object|String} Either the coverage object or, if a coverage
529
         *      format is specified, a string representing the results in that format.
530
         * @method getCoverage
531
         */
532
        getCoverage: function(format){
533
            if (!this._running && typeof _yuitest_coverage == "object"){
534
                if (YAHOO.lang.isFunction(format)){
535
                    return format(_yuitest_coverage);
536
                } else {
537
                    return _yuitest_coverage;
538
                }
539
            } else {
540
                return null;
541
            }
542
        },
543
 
544
        //-------------------------------------------------------------------------
545
        // Misc Methods
546
        //-------------------------------------------------------------------------
547
 
548
        /**
549
         * Retrieves the name of the current result set.
550
         * @return {String} The name of the result set.
551
         * @method getName
552
         */
553
        getName: function(){
554
            return this.masterSuite.name;
555
        },
556
 
557
        /**
558
         * The name assigned to the master suite of the TestRunner. This is the name
559
         * that is output as the root's name when results are retrieved.
560
         * @param {String} name The name of the result set.
561
         * @return {Void}
562
         * @method setName
563
         */
564
        setName: function(name){
565
            this.masterSuite.name = name;
566
        },
567
 
568
        //-------------------------------------------------------------------------
569
        // Test Tree-Related Methods
570
        //-------------------------------------------------------------------------
571
 
572
        /**
573
         * Adds a test case to the test tree as a child of the specified node.
574
         * @param {TestNode} parentNode The node to add the test case to as a child.
575
         * @param {YAHOO.tool.TestCase} testCase The test case to add.
576
         * @return {Void}
577
         * @static
578
         * @private
579
         * @method _addTestCaseToTestTree
580
         */
581
       _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
582
 
583
            //add the test suite
584
            var node = parentNode.appendChild(testCase);
585
 
586
            //iterate over the items in the test case
587
            for (var prop in testCase){
588
                if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
589
                    node.appendChild(prop);
590
                }
591
            }
592
 
593
        },
594
 
595
        /**
596
         * Adds a test suite to the test tree as a child of the specified node.
597
         * @param {TestNode} parentNode The node to add the test suite to as a child.
598
         * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
599
         * @return {Void}
600
         * @static
601
         * @private
602
         * @method _addTestSuiteToTestTree
603
         */
604
        _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
605
 
606
            //add the test suite
607
            var node = parentNode.appendChild(testSuite);
608
 
609
            //iterate over the items in the master suite
610
            for (var i=0; i < testSuite.items.length; i++){
611
                if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
612
                    this._addTestSuiteToTestTree(node, testSuite.items[i]);
613
                } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
614
                    this._addTestCaseToTestTree(node, testSuite.items[i]);
615
                }
616
            }
617
        },
618
 
619
        /**
620
         * Builds the test tree based on items in the master suite. The tree is a hierarchical
621
         * representation of the test suites, test cases, and test functions. The resulting tree
622
         * is stored in _root and the pointer _cur is set to the root initially.
623
         * @return {Void}
624
         * @static
625
         * @private
626
         * @method _buildTestTree
627
         */
628
        _buildTestTree : function () /*:Void*/ {
629
 
630
            this._root = new TestNode(this.masterSuite);
631
            //this._cur = this._root;
632
 
633
            //iterate over the items in the master suite
634
            for (var i=0; i < this.masterSuite.items.length; i++){
635
                if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
636
                    this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
637
                } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
638
                    this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
639
                }
640
            }
641
 
642
        },
643
 
644
        //-------------------------------------------------------------------------
645
        // Private Methods
646
        //-------------------------------------------------------------------------
647
 
648
        /**
649
         * Handles the completion of a test object's tests. Tallies test results
650
         * from one level up to the next.
651
         * @param {TestNode} node The TestNode representing the test object.
652
         * @return {Void}
653
         * @method _handleTestObjectComplete
654
         * @private
655
         * @static
656
         */
657
        _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
658
            if (YAHOO.lang.isObject(node.testObject)){
659
                node.parent.results.passed += node.results.passed;
660
                node.parent.results.failed += node.results.failed;
661
                node.parent.results.total += node.results.total;
662
                node.parent.results.ignored += node.results.ignored;
663
                node.parent.results[node.testObject.name] = node.results;
664
 
665
                if (node.testObject instanceof YAHOO.tool.TestSuite){
666
                    node.testObject.tearDown();
667
                    node.results.duration = (new Date()) - node._start;
668
                    this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
669
                } else if (node.testObject instanceof YAHOO.tool.TestCase){
670
                    node.results.duration = (new Date()) - node._start;
671
                    this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
672
                }
673
            }
674
        },
675
 
676
        //-------------------------------------------------------------------------
677
        // Navigation Methods
678
        //-------------------------------------------------------------------------
679
 
680
        /**
681
         * Retrieves the next node in the test tree.
682
         * @return {TestNode} The next node in the test tree or null if the end is reached.
683
         * @private
684
         * @static
685
         * @method _next
686
         */
687
        _next : function () /*:TestNode*/ {
688
 
689
            if (this._cur === null){
690
                this._cur = this._root;
691
            } else if (this._cur.firstChild) {
692
                this._cur = this._cur.firstChild;
693
            } else if (this._cur.next) {
694
                this._cur = this._cur.next;
695
            } else {
696
                while (this._cur && !this._cur.next && this._cur !== this._root){
697
                    this._handleTestObjectComplete(this._cur);
698
                    this._cur = this._cur.parent;
699
                }
700
 
701
                if (this._cur == this._root){
702
                    this._cur.results.type = "report";
703
                    this._cur.results.timestamp = (new Date()).toLocaleString();
704
                    this._cur.results.duration = (new Date()) - this._cur._start;
705
                    this._lastResults = this._cur.results;
706
                    this._running = false;
707
                    this.fireEvent(this.COMPLETE_EVENT, { results: this._lastResults});
708
                    this._cur = null;
709
                } else {
710
                    this._handleTestObjectComplete(this._cur);
711
                    this._cur = this._cur.next;
712
                }
713
            }
714
 
715
            return this._cur;
716
        },
717
 
718
        /**
719
         * Runs a test case or test suite, returning the results.
720
         * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
721
         * @return {Object} Results of the execution with properties passed, failed, and total.
722
         * @private
723
         * @method _run
724
         * @static
725
         */
726
        _run : function () /*:Void*/ {
727
 
728
            //flag to indicate if the TestRunner should wait before continuing
729
            var shouldWait = false;
730
 
731
            //get the next test node
732
            var node = this._next();
733
 
734
 
735
            if (node !== null) {
736
 
737
                //set flag to say the testrunner is running
738
                this._running = true;
739
 
740
                //eliminate last results
741
                this._lastResult = null;
742
 
743
                var testObject = node.testObject;
744
 
745
                //figure out what to do
746
                if (YAHOO.lang.isObject(testObject)){
747
                    if (testObject instanceof YAHOO.tool.TestSuite){
748
                        this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
749
                        node._start = new Date();
750
                        testObject.setUp();
751
                    } else if (testObject instanceof YAHOO.tool.TestCase){
752
                        this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
753
                        node._start = new Date();
754
                    }
755
 
756
                    //some environments don't support setTimeout
757
                    if (typeof setTimeout != "undefined"){
758
                        setTimeout(function(){
759
                            YAHOO.tool.TestRunner._run();
760
                        }, 0);
761
                    } else {
762
                        this._run();
763
                    }
764
                } else {
765
                    this._runTest(node);
766
                }
767
 
768
            }
769
        },
770
 
771
        _resumeTest : function (segment /*:Function*/) /*:Void*/ {
772
 
773
            //get relevant information
774
            var node /*:TestNode*/ = this._cur;
775
            var testName /*:String*/ = node.testObject;
776
            var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
777
 
778
            //cancel other waits if available
779
            if (testCase.__yui_wait){
780
                clearTimeout(testCase.__yui_wait);
781
                delete testCase.__yui_wait;
782
            }
783
 
784
            //get the "should" test cases
785
            var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
786
            var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
787
 
788
            //variable to hold whether or not the test failed
789
            var failed /*:Boolean*/ = false;
790
            var error /*:Error*/ = null;
791
 
792
            //try the test
793
            try {
794
 
795
                //run the test
796
                segment.apply(testCase);
797
 
798
                //if it should fail, and it got here, then it's a fail because it didn't
799
                if (shouldFail){
800
                    error = new YAHOO.util.ShouldFail();
801
                    failed = true;
802
                } else if (shouldError){
803
                    error = new YAHOO.util.ShouldError();
804
                    failed = true;
805
                }
806
 
807
            } catch (thrown /*:Error*/){
808
                if (thrown instanceof YAHOO.util.AssertionError) {
809
                    if (!shouldFail){
810
                        error = thrown;
811
                        failed = true;
812
                    }
813
                } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
814
 
815
                    if (YAHOO.lang.isFunction(thrown.segment)){
816
                        if (YAHOO.lang.isNumber(thrown.delay)){
817
 
818
                            //some environments don't support setTimeout
819
                            if (typeof setTimeout != "undefined"){
820
                                testCase.__yui_wait = setTimeout(function(){
821
                                    YAHOO.tool.TestRunner._resumeTest(thrown.segment);
822
                                }, thrown.delay);
823
                            } else {
824
                                throw new Error("Asynchronous tests not supported in this environment.");
825
                            }
826
                        }
827
                    }
828
 
829
                    return;
830
 
831
                } else {
832
                    //first check to see if it should error
833
                    if (!shouldError) {
834
                        error = new YAHOO.util.UnexpectedError(thrown);
835
                        failed = true;
836
                    } else {
837
                        //check to see what type of data we have
838
                        if (YAHOO.lang.isString(shouldError)){
839
 
840
                            //if it's a string, check the error message
841
                            if (thrown.message != shouldError){
842
                                error = new YAHOO.util.UnexpectedError(thrown);
843
                                failed = true;
844
                            }
845
                        } else if (YAHOO.lang.isFunction(shouldError)){
846
 
847
                            //if it's a function, see if the error is an instance of it
848
                            if (!(thrown instanceof shouldError)){
849
                                error = new YAHOO.util.UnexpectedError(thrown);
850
                                failed = true;
851
                            }
852
 
853
                        } else if (YAHOO.lang.isObject(shouldError)){
854
 
855
                            //if it's an object, check the instance and message
856
                            if (!(thrown instanceof shouldError.constructor) ||
857
                                    thrown.message != shouldError.message){
858
                                error = new YAHOO.util.UnexpectedError(thrown);
859
                                failed = true;
860
                            }
861
 
862
                        }
863
 
864
                    }
865
                }
866
 
867
            }
868
 
869
            //fireEvent appropriate event
870
            if (failed) {
871
                this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
872
            } else {
873
                this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
874
            }
875
 
876
            //run the tear down
877
            testCase.tearDown();
878
 
879
            //calculate duration
880
            var duration = (new Date()) - node._start;
881
 
882
            //update results
883
            node.parent.results[testName] = {
884
                result: failed ? "fail" : "pass",
885
                message: error ? error.getMessage() : "Test passed",
886
                type: "test",
887
                name: testName,
888
                duration: duration
889
            };
890
 
891
            if (failed){
892
                node.parent.results.failed++;
893
            } else {
894
                node.parent.results.passed++;
895
            }
896
            node.parent.results.total++;
897
 
898
            //set timeout not supported in all environments
899
            if (typeof setTimeout != "undefined"){
900
                setTimeout(function(){
901
                    YAHOO.tool.TestRunner._run();
902
                }, 0);
903
            } else {
904
                this._run();
905
            }
906
 
907
        },
908
 
909
        /**
910
         * Runs a single test based on the data provided in the node.
911
         * @param {TestNode} node The TestNode representing the test to run.
912
         * @return {Void}
913
         * @static
914
         * @private
915
         * @name _runTest
916
         */
917
        _runTest : function (node /*:TestNode*/) /*:Void*/ {
918
 
919
            //get relevant information
920
            var testName /*:String*/ = node.testObject;
921
            var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
922
            var test /*:Function*/ = testCase[testName];
923
 
924
            //get the "should" test cases
925
            var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
926
 
927
            //figure out if the test should be ignored or not
928
            if (shouldIgnore){
929
 
930
                //update results
931
                node.parent.results[testName] = {
932
                    result: "ignore",
933
                    message: "Test ignored",
934
                    type: "test",
935
                    name: testName
936
                };
937
 
938
                node.parent.results.ignored++;
939
                node.parent.results.total++;
940
 
941
                this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
942
 
943
                //some environments don't support setTimeout
944
                if (typeof setTimeout != "undefined"){
945
                    setTimeout(function(){
946
                        YAHOO.tool.TestRunner._run();
947
                    }, 0);
948
                } else {
949
                    this._run();
950
                }
951
 
952
            } else {
953
 
954
                //mark the start time
955
                node._start = new Date();
956
 
957
                //run the setup
958
                testCase.setUp();
959
 
960
                //now call the body of the test
961
                this._resumeTest(test);
962
            }
963
 
964
        },
965
 
966
        //-------------------------------------------------------------------------
967
        // Protected Methods
968
        //-------------------------------------------------------------------------
969
 
970
        /**
971
         * Fires events for the TestRunner. This overrides the default fireEvent()
972
         * method from EventProvider to add the type property to the data that is
973
         * passed through on each event call.
974
         * @param {String} type The type of event to fire.
975
         * @param {Object} data (Optional) Data for the event.
976
         * @method fireEvent
977
         * @static
978
         * @protected
979
         */
980
        fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
981
            data = data || {};
982
            data.type = type;
983
            TestRunner.superclass.fireEvent.call(this, type, data);
984
        },
985
 
986
        //-------------------------------------------------------------------------
987
        // Public Methods
988
        //-------------------------------------------------------------------------
989
 
990
        /**
991
         * Adds a test suite or test case to the list of test objects to run.
992
         * @param testObject Either a TestCase or a TestSuite that should be run.
993
         * @return {Void}
994
         * @method add
995
         * @static
996
         */
997
        add : function (testObject /*:Object*/) /*:Void*/ {
998
            this.masterSuite.add(testObject);
999
        },
1000
 
1001
        /**
1002
         * Removes all test objects from the runner.
1003
         * @return {Void}
1004
         * @method clear
1005
         * @static
1006
         */
1007
        clear : function () /*:Void*/ {
1008
            this.masterSuite = new YAHOO.tool.TestSuite("yuitests" + (new Date()).getTime());
1009
        },
1010
 
1011
        /**
1012
         * Resumes the TestRunner after wait() was called.
1013
         * @param {Function} segment The function to run as the rest
1014
         *      of the haulted test.
1015
         * @return {Void}
1016
         * @method resume
1017
         * @static
1018
         */
1019
        resume : function (segment /*:Function*/) /*:Void*/ {
1020
            this._resumeTest(segment || function(){});
1021
        },
1022
 
1023
        /**
1024
         * Runs the test suite.
1025
         * @param {Boolean} oldMode (Optional) Specifies that the <= 2.8 way of
1026
         *      internally managing test suites should be used.
1027
         * @return {Void}
1028
         * @method run
1029
         * @static
1030
         */
1031
        run : function (oldMode) {
1032
 
1033
            //pointer to runner to avoid scope issues
1034
            var runner = YAHOO.tool.TestRunner;
1035
 
1036
            //if there's only one suite on the masterSuite, move it up
1037
            if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YAHOO.tool.TestSuite){
1038
                this.masterSuite = this.masterSuite.items[0];
1039
            }
1040
 
1041
            //build the test tree
1042
            runner._buildTestTree();
1043
 
1044
            //set when the test started
1045
            runner._root._start = new Date();
1046
 
1047
            //fire the begin event
1048
            runner.fireEvent(runner.BEGIN_EVENT);
1049
 
1050
            //begin the testing
1051
            runner._run();
1052
        }
1053
    });
1054
 
1055
    return new TestRunner();
1056
 
1057
})();
1058
YAHOO.namespace("util");
1059
 
1060
//-----------------------------------------------------------------------------
1061
// Assert object
1062
//-----------------------------------------------------------------------------
1063
 
1064
/**
1065
 * The Assert object provides functions to test JavaScript values against
1066
 * known and expected results. Whenever a comparison (assertion) fails,
1067
 * an error is thrown.
1068
 *
1069
 * @namespace YAHOO.util
1070
 * @class Assert
1071
 * @static
1072
 */
1073
YAHOO.util.Assert = {
1074
 
1075
    //-------------------------------------------------------------------------
1076
    // Helper Methods
1077
    //-------------------------------------------------------------------------
1078
 
1079
    /**
1080
     * Formats a message so that it can contain the original assertion message
1081
     * in addition to the custom message.
1082
     * @param {String} customMessage The message passed in by the developer.
1083
     * @param {String} defaultMessage The message created by the error by default.
1084
     * @return {String} The final error message, containing either or both.
1085
     * @protected
1086
     * @static
1087
     * @method _formatMessage
1088
     */
1089
    _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
1090
        var message = customMessage;
1091
        if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
1092
            return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
1093
        } else {
1094
            return defaultMessage;
1095
        }
1096
    },
1097
 
1098
    //-------------------------------------------------------------------------
1099
    // Generic Assertion Methods
1100
    //-------------------------------------------------------------------------
1101
 
1102
    /**
1103
     * Forces an assertion error to occur.
1104
     * @param {String} message (Optional) The message to display with the failure.
1105
     * @method fail
1106
     * @static
1107
     */
1108
    fail : function (message /*:String*/) /*:Void*/ {
1109
        throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
1110
    },
1111
 
1112
    //-------------------------------------------------------------------------
1113
    // Equality Assertion Methods
1114
    //-------------------------------------------------------------------------
1115
 
1116
    /**
1117
     * Asserts that a value is equal to another. This uses the double equals sign
1118
     * so type coercion may occur.
1119
     * @param {Object} expected The expected value.
1120
     * @param {Object} actual The actual value to test.
1121
     * @param {String} message (Optional) The message to display if the assertion fails.
1122
     * @method areEqual
1123
     * @static
1124
     */
1125
    areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1126
        if (expected != actual) {
1127
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
1128
        }
1129
    },
1130
 
1131
    /**
1132
     * Asserts that a value is not equal to another. This uses the double equals sign
1133
     * so type coercion may occur.
1134
     * @param {Object} unexpected The unexpected value.
1135
     * @param {Object} actual The actual value to test.
1136
     * @param {String} message (Optional) The message to display if the assertion fails.
1137
     * @method areNotEqual
1138
     * @static
1139
     */
1140
    areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/,
1141
                         message /*:String*/) /*:Void*/ {
1142
        if (unexpected == actual) {
1143
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
1144
        }
1145
    },
1146
 
1147
    /**
1148
     * Asserts that a value is not the same as another. This uses the triple equals sign
1149
     * so no type coercion may occur.
1150
     * @param {Object} unexpected The unexpected value.
1151
     * @param {Object} actual The actual value to test.
1152
     * @param {String} message (Optional) The message to display if the assertion fails.
1153
     * @method areNotSame
1154
     * @static
1155
     */
1156
    areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1157
        if (unexpected === actual) {
1158
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1159
        }
1160
    },
1161
 
1162
    /**
1163
     * Asserts that a value is the same as another. This uses the triple equals sign
1164
     * so no type coercion may occur.
1165
     * @param {Object} expected The expected value.
1166
     * @param {Object} actual The actual value to test.
1167
     * @param {String} message (Optional) The message to display if the assertion fails.
1168
     * @method areSame
1169
     * @static
1170
     */
1171
    areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1172
        if (expected !== actual) {
1173
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1174
        }
1175
    },
1176
 
1177
    //-------------------------------------------------------------------------
1178
    // Boolean Assertion Methods
1179
    //-------------------------------------------------------------------------
1180
 
1181
    /**
1182
     * Asserts that a value is false. This uses the triple equals sign
1183
     * so no type coercion may occur.
1184
     * @param {Object} actual The actual value to test.
1185
     * @param {String} message (Optional) The message to display if the assertion fails.
1186
     * @method isFalse
1187
     * @static
1188
     */
1189
    isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1190
        if (false !== actual) {
1191
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1192
        }
1193
    },
1194
 
1195
    /**
1196
     * Asserts that a value is true. This uses the triple equals sign
1197
     * so no type coercion may occur.
1198
     * @param {Object} actual The actual value to test.
1199
     * @param {String} message (Optional) The message to display if the assertion fails.
1200
     * @method isTrue
1201
     * @static
1202
     */
1203
    isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1204
        if (true !== actual) {
1205
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1206
        }
1207
 
1208
    },
1209
 
1210
    //-------------------------------------------------------------------------
1211
    // Special Value Assertion Methods
1212
    //-------------------------------------------------------------------------
1213
 
1214
    /**
1215
     * Asserts that a value is not a number.
1216
     * @param {Object} actual The value to test.
1217
     * @param {String} message (Optional) The message to display if the assertion fails.
1218
     * @method isNaN
1219
     * @static
1220
     */
1221
    isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1222
        if (!isNaN(actual)){
1223
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1224
        }
1225
    },
1226
 
1227
    /**
1228
     * Asserts that a value is not the special NaN value.
1229
     * @param {Object} actual The value to test.
1230
     * @param {String} message (Optional) The message to display if the assertion fails.
1231
     * @method isNotNaN
1232
     * @static
1233
     */
1234
    isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1235
        if (isNaN(actual)){
1236
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1237
        }
1238
    },
1239
 
1240
    /**
1241
     * Asserts that a value is not null. This uses the triple equals sign
1242
     * so no type coercion may occur.
1243
     * @param {Object} actual The actual value to test.
1244
     * @param {String} message (Optional) The message to display if the assertion fails.
1245
     * @method isNotNull
1246
     * @static
1247
     */
1248
    isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1249
        if (YAHOO.lang.isNull(actual)) {
1250
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1251
        }
1252
    },
1253
 
1254
    /**
1255
     * Asserts that a value is not undefined. This uses the triple equals sign
1256
     * so no type coercion may occur.
1257
     * @param {Object} actual The actual value to test.
1258
     * @param {String} message (Optional) The message to display if the assertion fails.
1259
     * @method isNotUndefined
1260
     * @static
1261
     */
1262
    isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1263
        if (YAHOO.lang.isUndefined(actual)) {
1264
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1265
        }
1266
    },
1267
 
1268
    /**
1269
     * Asserts that a value is null. This uses the triple equals sign
1270
     * so no type coercion may occur.
1271
     * @param {Object} actual The actual value to test.
1272
     * @param {String} message (Optional) The message to display if the assertion fails.
1273
     * @method isNull
1274
     * @static
1275
     */
1276
    isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1277
        if (!YAHOO.lang.isNull(actual)) {
1278
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1279
        }
1280
    },
1281
 
1282
    /**
1283
     * Asserts that a value is undefined. This uses the triple equals sign
1284
     * so no type coercion may occur.
1285
     * @param {Object} actual The actual value to test.
1286
     * @param {String} message (Optional) The message to display if the assertion fails.
1287
     * @method isUndefined
1288
     * @static
1289
     */
1290
    isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1291
        if (!YAHOO.lang.isUndefined(actual)) {
1292
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1293
        }
1294
    },
1295
 
1296
    //--------------------------------------------------------------------------
1297
    // Instance Assertion Methods
1298
    //--------------------------------------------------------------------------
1299
 
1300
    /**
1301
     * Asserts that a value is an array.
1302
     * @param {Object} actual The value to test.
1303
     * @param {String} message (Optional) The message to display if the assertion fails.
1304
     * @method isArray
1305
     * @static
1306
     */
1307
    isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1308
        if (!YAHOO.lang.isArray(actual)){
1309
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1310
        }
1311
    },
1312
 
1313
    /**
1314
     * Asserts that a value is a Boolean.
1315
     * @param {Object} actual The value to test.
1316
     * @param {String} message (Optional) The message to display if the assertion fails.
1317
     * @method isBoolean
1318
     * @static
1319
     */
1320
    isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1321
        if (!YAHOO.lang.isBoolean(actual)){
1322
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1323
        }
1324
    },
1325
 
1326
    /**
1327
     * Asserts that a value is a function.
1328
     * @param {Object} actual The value to test.
1329
     * @param {String} message (Optional) The message to display if the assertion fails.
1330
     * @method isFunction
1331
     * @static
1332
     */
1333
    isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1334
        if (!YAHOO.lang.isFunction(actual)){
1335
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1336
        }
1337
    },
1338
 
1339
    /**
1340
     * Asserts that a value is an instance of a particular object. This may return
1341
     * incorrect results when comparing objects from one frame to constructors in
1342
     * another frame. For best results, don't use in a cross-frame manner.
1343
     * @param {Function} expected The function that the object should be an instance of.
1344
     * @param {Object} actual The object to test.
1345
     * @param {String} message (Optional) The message to display if the assertion fails.
1346
     * @method isInstanceOf
1347
     * @static
1348
     */
1349
    isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1350
        if (!(actual instanceof expected)){
1351
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1352
        }
1353
    },
1354
 
1355
    /**
1356
     * Asserts that a value is a number.
1357
     * @param {Object} actual The value to test.
1358
     * @param {String} message (Optional) The message to display if the assertion fails.
1359
     * @method isNumber
1360
     * @static
1361
     */
1362
    isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1363
        if (!YAHOO.lang.isNumber(actual)){
1364
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1365
        }
1366
    },
1367
 
1368
    /**
1369
     * Asserts that a value is an object.
1370
     * @param {Object} actual The value to test.
1371
     * @param {String} message (Optional) The message to display if the assertion fails.
1372
     * @method isObject
1373
     * @static
1374
     */
1375
    isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1376
        if (!YAHOO.lang.isObject(actual)){
1377
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1378
        }
1379
    },
1380
 
1381
    /**
1382
     * Asserts that a value is a string.
1383
     * @param {Object} actual The value to test.
1384
     * @param {String} message (Optional) The message to display if the assertion fails.
1385
     * @method isString
1386
     * @static
1387
     */
1388
    isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1389
        if (!YAHOO.lang.isString(actual)){
1390
            throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1391
        }
1392
    },
1393
 
1394
    /**
1395
     * Asserts that a value is of a particular type.
1396
     * @param {String} expectedType The expected type of the variable.
1397
     * @param {Object} actualValue The actual value to test.
1398
     * @param {String} message (Optional) The message to display if the assertion fails.
1399
     * @method isTypeOf
1400
     * @static
1401
     */
1402
    isTypeOf : function (expected /*:String*/, actual /*:Object*/, message /*:String*/) /*:Void*/{
1403
        if (typeof actual != expected){
1404
            throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1405
        }
1406
    }
1407
};
1408
 
1409
//-----------------------------------------------------------------------------
1410
// Assertion errors
1411
//-----------------------------------------------------------------------------
1412
 
1413
/**
1414
 * AssertionError is thrown whenever an assertion fails. It provides methods
1415
 * to more easily get at error information and also provides a base class
1416
 * from which more specific assertion errors can be derived.
1417
 *
1418
 * @param {String} message The message to display when the error occurs.
1419
 * @namespace YAHOO.util
1420
 * @class AssertionError
1421
 * @extends Error
1422
 * @constructor
1423
 */
1424
YAHOO.util.AssertionError = function (message /*:String*/){
1425
 
1426
    //call superclass
1427
    //arguments.callee.superclass.constructor.call(this, message);
1428
 
1429
    /*
1430
     * Error message. Must be duplicated to ensure browser receives it.
1431
     * @type String
1432
     * @property message
1433
     */
1434
    this.message /*:String*/ = message;
1435
 
1436
    /**
1437
     * The name of the error that occurred.
1438
     * @type String
1439
     * @property name
1440
     */
1441
    this.name /*:String*/ = "AssertionError";
1442
};
1443
 
1444
//inherit methods
1445
YAHOO.lang.extend(YAHOO.util.AssertionError, Object, {
1446
 
1447
    /**
1448
     * Returns a fully formatted error for an assertion failure. This should
1449
     * be overridden by all subclasses to provide specific information.
1450
     * @method getMessage
1451
     * @return {String} A string describing the error.
1452
     */
1453
    getMessage : function () /*:String*/ {
1454
        return this.message;
1455
    },
1456
 
1457
    /**
1458
     * Returns a string representation of the error.
1459
     * @method toString
1460
     * @return {String} A string representation of the error.
1461
     */
1462
    toString : function () /*:String*/ {
1463
        return this.name + ": " + this.getMessage();
1464
    }
1465
 
1466
});
1467
 
1468
/**
1469
 * ComparisonFailure is subclass of AssertionError that is thrown whenever
1470
 * a comparison between two values fails. It provides mechanisms to retrieve
1471
 * both the expected and actual value.
1472
 *
1473
 * @param {String} message The message to display when the error occurs.
1474
 * @param {Object} expected The expected value.
1475
 * @param {Object} actual The actual value that caused the assertion to fail.
1476
 * @namespace YAHOO.util
1477
 * @extends YAHOO.util.AssertionError
1478
 * @class ComparisonFailure
1479
 * @constructor
1480
 */
1481
YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1482
 
1483
    //call superclass
1484
    YAHOO.util.AssertionError.call(this, message);
1485
 
1486
    /**
1487
     * The expected value.
1488
     * @type Object
1489
     * @property expected
1490
     */
1491
    this.expected /*:Object*/ = expected;
1492
 
1493
    /**
1494
     * The actual value.
1495
     * @type Object
1496
     * @property actual
1497
     */
1498
    this.actual /*:Object*/ = actual;
1499
 
1500
    /**
1501
     * The name of the error that occurred.
1502
     * @type String
1503
     * @property name
1504
     */
1505
    this.name /*:String*/ = "ComparisonFailure";
1506
 
1507
};
1508
 
1509
//inherit methods
1510
YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1511
 
1512
    /**
1513
     * Returns a fully formatted error for an assertion failure. This message
1514
     * provides information about the expected and actual values.
1515
     * @method toString
1516
     * @return {String} A string describing the error.
1517
     */
1518
    getMessage : function () /*:String*/ {
1519
        return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
1520
            "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1521
    }
1522
 
1523
});
1524
 
1525
/**
1526
 * UnexpectedValue is subclass of AssertionError that is thrown whenever
1527
 * a value was unexpected in its scope. This typically means that a test
1528
 * was performed to determine that a value was *not* equal to a certain
1529
 * value.
1530
 *
1531
 * @param {String} message The message to display when the error occurs.
1532
 * @param {Object} unexpected The unexpected value.
1533
 * @namespace YAHOO.util
1534
 * @extends YAHOO.util.AssertionError
1535
 * @class UnexpectedValue
1536
 * @constructor
1537
 */
1538
YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1539
 
1540
    //call superclass
1541
    YAHOO.util.AssertionError.call(this, message);
1542
 
1543
    /**
1544
     * The unexpected value.
1545
     * @type Object
1546
     * @property unexpected
1547
     */
1548
    this.unexpected /*:Object*/ = unexpected;
1549
 
1550
    /**
1551
     * The name of the error that occurred.
1552
     * @type String
1553
     * @property name
1554
     */
1555
    this.name /*:String*/ = "UnexpectedValue";
1556
 
1557
};
1558
 
1559
//inherit methods
1560
YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1561
 
1562
    /**
1563
     * Returns a fully formatted error for an assertion failure. The message
1564
     * contains information about the unexpected value that was encountered.
1565
     * @method getMessage
1566
     * @return {String} A string describing the error.
1567
     */
1568
    getMessage : function () /*:String*/ {
1569
        return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1570
    }
1571
 
1572
});
1573
 
1574
/**
1575
 * ShouldFail is subclass of AssertionError that is thrown whenever
1576
 * a test was expected to fail but did not.
1577
 *
1578
 * @param {String} message The message to display when the error occurs.
1579
 * @namespace YAHOO.util
1580
 * @extends YAHOO.util.AssertionError
1581
 * @class ShouldFail
1582
 * @constructor
1583
 */
1584
YAHOO.util.ShouldFail = function (message /*:String*/){
1585
 
1586
    //call superclass
1587
    YAHOO.util.AssertionError.call(this, message || "This test should fail but didn't.");
1588
 
1589
    /**
1590
     * The name of the error that occurred.
1591
     * @type String
1592
     * @property name
1593
     */
1594
    this.name /*:String*/ = "ShouldFail";
1595
 
1596
};
1597
 
1598
//inherit methods
1599
YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1600
 
1601
/**
1602
 * ShouldError is subclass of AssertionError that is thrown whenever
1603
 * a test is expected to throw an error but doesn't.
1604
 *
1605
 * @param {String} message The message to display when the error occurs.
1606
 * @namespace YAHOO.util
1607
 * @extends YAHOO.util.AssertionError
1608
 * @class ShouldError
1609
 * @constructor
1610
 */
1611
YAHOO.util.ShouldError = function (message /*:String*/){
1612
 
1613
    //call superclass
1614
    YAHOO.util.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
1615
 
1616
    /**
1617
     * The name of the error that occurred.
1618
     * @type String
1619
     * @property name
1620
     */
1621
    this.name /*:String*/ = "ShouldError";
1622
 
1623
};
1624
 
1625
//inherit methods
1626
YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1627
 
1628
/**
1629
 * UnexpectedError is subclass of AssertionError that is thrown whenever
1630
 * an error occurs within the course of a test and the test was not expected
1631
 * to throw an error.
1632
 *
1633
 * @param {Error} cause The unexpected error that caused this error to be
1634
 *                      thrown.
1635
 * @namespace YAHOO.util
1636
 * @extends YAHOO.util.AssertionError
1637
 * @class UnexpectedError
1638
 * @constructor
1639
 */
1640
YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1641
 
1642
    //call superclass
1643
    YAHOO.util.AssertionError.call(this, "Unexpected error: " + cause.message);
1644
 
1645
    /**
1646
     * The unexpected error that occurred.
1647
     * @type Error
1648
     * @property cause
1649
     */
1650
    this.cause /*:Error*/ = cause;
1651
 
1652
    /**
1653
     * The name of the error that occurred.
1654
     * @type String
1655
     * @property name
1656
     */
1657
    this.name /*:String*/ = "UnexpectedError";
1658
 
1659
    /**
1660
     * Stack information for the error (if provided).
1661
     * @type String
1662
     * @property stack
1663
     */
1664
    this.stack /*:String*/ = cause.stack;
1665
 
1666
};
1667
 
1668
//inherit methods
1669
YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1670
//-----------------------------------------------------------------------------
1671
// ArrayAssert object
1672
//-----------------------------------------------------------------------------
1673
 
1674
/**
1675
 * The ArrayAssert object provides functions to test JavaScript array objects
1676
 * for a variety of cases.
1677
 *
1678
 * @namespace YAHOO.util
1679
 * @class ArrayAssert
1680
 * @static
1681
 */
1682
 
1683
YAHOO.util.ArrayAssert = {
1684
 
1685
    /**
1686
     * Asserts that a value is present in an array. This uses the triple equals
1687
     * sign so no type coercion may occur.
1688
     * @param {Object} needle The value that is expected in the array.
1689
     * @param {Array} haystack An array of values.
1690
     * @param {String} message (Optional) The message to display if the assertion fails.
1691
     * @method contains
1692
     * @static
1693
     */
1694
    contains : function (needle /*:Object*/, haystack /*:Array*/,
1695
                           message /*:String*/) /*:Void*/ {
1696
 
1697
        var found /*:Boolean*/ = false;
1698
        var Assert = YAHOO.util.Assert;
1699
 
1700
        //begin checking values
1701
        for (var i=0; i < haystack.length && !found; i++){
1702
            if (haystack[i] === needle) {
1703
                found = true;
1704
            }
1705
        }
1706
 
1707
        if (!found){
1708
            Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1709
        }
1710
    },
1711
 
1712
    /**
1713
     * Asserts that a set of values are present in an array. This uses the triple equals
1714
     * sign so no type coercion may occur. For this assertion to pass, all values must
1715
     * be found.
1716
     * @param {Object[]} needles An array of values that are expected in the array.
1717
     * @param {Array} haystack An array of values to check.
1718
     * @param {String} message (Optional) The message to display if the assertion fails.
1719
     * @method containsItems
1720
     * @static
1721
     */
1722
    containsItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1723
                           message /*:String*/) /*:Void*/ {
1724
 
1725
        //begin checking values
1726
        for (var i=0; i < needles.length; i++){
1727
            this.contains(needles[i], haystack, message);
1728
        }
1729
    },
1730
 
1731
    /**
1732
     * Asserts that a value matching some condition is present in an array. This uses
1733
     * a function to determine a match.
1734
     * @param {Function} matcher A function that returns true if the items matches or false if not.
1735
     * @param {Array} haystack An array of values.
1736
     * @param {String} message (Optional) The message to display if the assertion fails.
1737
     * @method containsMatch
1738
     * @static
1739
     */
1740
    containsMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1741
                           message /*:String*/) /*:Void*/ {
1742
 
1743
        //check for valid matcher
1744
        if (typeof matcher != "function"){
1745
            throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1746
        }
1747
 
1748
        var found /*:Boolean*/ = false;
1749
        var Assert = YAHOO.util.Assert;
1750
 
1751
        //begin checking values
1752
        for (var i=0; i < haystack.length && !found; i++){
1753
            if (matcher(haystack[i])) {
1754
                found = true;
1755
            }
1756
        }
1757
 
1758
        if (!found){
1759
            Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1760
        }
1761
    },
1762
 
1763
    /**
1764
     * Asserts that a value is not present in an array. This uses the triple equals
1765
     * sign so no type coercion may occur.
1766
     * @param {Object} needle The value that is expected in the array.
1767
     * @param {Array} haystack An array of values.
1768
     * @param {String} message (Optional) The message to display if the assertion fails.
1769
     * @method doesNotContain
1770
     * @static
1771
     */
1772
    doesNotContain : function (needle /*:Object*/, haystack /*:Array*/,
1773
                           message /*:String*/) /*:Void*/ {
1774
 
1775
        var found /*:Boolean*/ = false;
1776
        var Assert = YAHOO.util.Assert;
1777
 
1778
        //begin checking values
1779
        for (var i=0; i < haystack.length && !found; i++){
1780
            if (haystack[i] === needle) {
1781
                found = true;
1782
            }
1783
        }
1784
 
1785
        if (found){
1786
            Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1787
        }
1788
    },
1789
 
1790
    /**
1791
     * Asserts that a set of values are not present in an array. This uses the triple equals
1792
     * sign so no type coercion may occur. For this assertion to pass, all values must
1793
     * not be found.
1794
     * @param {Object[]} needles An array of values that are not expected in the array.
1795
     * @param {Array} haystack An array of values to check.
1796
     * @param {String} message (Optional) The message to display if the assertion fails.
1797
     * @method doesNotContainItems
1798
     * @static
1799
     */
1800
    doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1801
                           message /*:String*/) /*:Void*/ {
1802
 
1803
        for (var i=0; i < needles.length; i++){
1804
            this.doesNotContain(needles[i], haystack, message);
1805
        }
1806
 
1807
    },
1808
 
1809
    /**
1810
     * Asserts that no values matching a condition are present in an array. This uses
1811
     * a function to determine a match.
1812
     * @param {Function} matcher A function that returns true if the items matches or false if not.
1813
     * @param {Array} haystack An array of values.
1814
     * @param {String} message (Optional) The message to display if the assertion fails.
1815
     * @method doesNotContainMatch
1816
     * @static
1817
     */
1818
    doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1819
                           message /*:String*/) /*:Void*/ {
1820
 
1821
        //check for valid matcher
1822
        if (typeof matcher != "function"){
1823
            throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1824
        }
1825
 
1826
        var found /*:Boolean*/ = false;
1827
        var Assert = YAHOO.util.Assert;
1828
 
1829
        //begin checking values
1830
        for (var i=0; i < haystack.length && !found; i++){
1831
            if (matcher(haystack[i])) {
1832
                found = true;
1833
            }
1834
        }
1835
 
1836
        if (found){
1837
            Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1838
        }
1839
    },
1840
 
1841
    /**
1842
     * Asserts that the given value is contained in an array at the specified index.
1843
     * This uses the triple equals sign so no type coercion will occur.
1844
     * @param {Object} needle The value to look for.
1845
     * @param {Array} haystack The array to search in.
1846
     * @param {int} index The index at which the value should exist.
1847
     * @param {String} message (Optional) The message to display if the assertion fails.
1848
     * @method indexOf
1849
     * @static
1850
     */
1851
    indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1852
 
1853
        //try to find the value in the array
1854
        for (var i=0; i < haystack.length; i++){
1855
            if (haystack[i] === needle){
1856
                YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1857
                return;
1858
            }
1859
        }
1860
 
1861
        var Assert = YAHOO.util.Assert;
1862
 
1863
        //if it makes it here, it wasn't found at all
1864
        Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1865
    },
1866
 
1867
    /**
1868
     * Asserts that the values in an array are equal, and in the same position,
1869
     * as values in another array. This uses the double equals sign
1870
     * so type coercion may occur. Note that the array objects themselves
1871
     * need not be the same for this test to pass.
1872
     * @param {Array} expected An array of the expected values.
1873
     * @param {Array} actual Any array of the actual values.
1874
     * @param {String} message (Optional) The message to display if the assertion fails.
1875
     * @method itemsAreEqual
1876
     * @static
1877
     */
1878
    itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/,
1879
                           message /*:String*/) /*:Void*/ {
1880
 
1881
        //one may be longer than the other, so get the maximum length
1882
        var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1883
        var Assert = YAHOO.util.Assert;
1884
 
1885
        //begin checking values
1886
        for (var i=0; i < len; i++){
1887
            Assert.areEqual(expected[i], actual[i],
1888
                Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1889
        }
1890
    },
1891
 
1892
    /**
1893
     * Asserts that the values in an array are equivalent, and in the same position,
1894
     * as values in another array. This uses a function to determine if the values
1895
     * are equivalent. Note that the array objects themselves
1896
     * need not be the same for this test to pass.
1897
     * @param {Array} expected An array of the expected values.
1898
     * @param {Array} actual Any array of the actual values.
1899
     * @param {Function} comparator A function that returns true if the values are equivalent
1900
     *      or false if not.
1901
     * @param {String} message (Optional) The message to display if the assertion fails.
1902
     * @return {Void}
1903
     * @method itemsAreEquivalent
1904
     * @static
1905
     */
1906
    itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/,
1907
                           comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1908
 
1909
        //make sure the comparator is valid
1910
        if (typeof comparator != "function"){
1911
            throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1912
        }
1913
 
1914
        //one may be longer than the other, so get the maximum length
1915
        var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1916
 
1917
        //begin checking values
1918
        for (var i=0; i < len; i++){
1919
            if (!comparator(expected[i], actual[i])){
1920
                throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1921
            }
1922
        }
1923
    },
1924
 
1925
    /**
1926
     * Asserts that an array is empty.
1927
     * @param {Array} actual The array to test.
1928
     * @param {String} message (Optional) The message to display if the assertion fails.
1929
     * @method isEmpty
1930
     * @static
1931
     */
1932
    isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1933
        if (actual.length > 0){
1934
            var Assert = YAHOO.util.Assert;
1935
            Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1936
        }
1937
    },
1938
 
1939
    /**
1940
     * Asserts that an array is not empty.
1941
     * @param {Array} actual The array to test.
1942
     * @param {String} message (Optional) The message to display if the assertion fails.
1943
     * @method isNotEmpty
1944
     * @static
1945
     */
1946
    isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1947
        if (actual.length === 0){
1948
            var Assert = YAHOO.util.Assert;
1949
            Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1950
        }
1951
    },
1952
 
1953
    /**
1954
     * Asserts that the values in an array are the same, and in the same position,
1955
     * as values in another array. This uses the triple equals sign
1956
     * so no type coercion will occur. Note that the array objects themselves
1957
     * need not be the same for this test to pass.
1958
     * @param {Array} expected An array of the expected values.
1959
     * @param {Array} actual Any array of the actual values.
1960
     * @param {String} message (Optional) The message to display if the assertion fails.
1961
     * @method itemsAreSame
1962
     * @static
1963
     */
1964
    itemsAreSame : function (expected /*:Array*/, actual /*:Array*/,
1965
                          message /*:String*/) /*:Void*/ {
1966
 
1967
        //one may be longer than the other, so get the maximum length
1968
        var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1969
        var Assert = YAHOO.util.Assert;
1970
 
1971
        //begin checking values
1972
        for (var i=0; i < len; i++){
1973
            Assert.areSame(expected[i], actual[i],
1974
                Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1975
        }
1976
    },
1977
 
1978
    /**
1979
     * Asserts that the given value is contained in an array at the specified index,
1980
     * starting from the back of the array.
1981
     * This uses the triple equals sign so no type coercion will occur.
1982
     * @param {Object} needle The value to look for.
1983
     * @param {Array} haystack The array to search in.
1984
     * @param {int} index The index at which the value should exist.
1985
     * @param {String} message (Optional) The message to display if the assertion fails.
1986
     * @method lastIndexOf
1987
     * @static
1988
     */
1989
    lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1990
 
1991
        var Assert = YAHOO.util.Assert;
1992
 
1993
        //try to find the value in the array
1994
        for (var i=haystack.length; i >= 0; i--){
1995
            if (haystack[i] === needle){
1996
                Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1997
                return;
1998
            }
1999
        }
2000
 
2001
        //if it makes it here, it wasn't found at all
2002
        Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));
2003
    }
2004
 
2005
};
2006
YAHOO.namespace("util");
2007
 
2008
 
2009
//-----------------------------------------------------------------------------
2010
// ObjectAssert object
2011
//-----------------------------------------------------------------------------
2012
 
2013
/**
2014
 * The ObjectAssert object provides functions to test JavaScript objects
2015
 * for a variety of cases.
2016
 *
2017
 * @namespace YAHOO.util
2018
 * @class ObjectAssert
2019
 * @static
2020
 */
2021
YAHOO.util.ObjectAssert = {
2022
 
2023
    /**
2024
     * Asserts that all properties in the object exist in another object.
2025
     * @param {Object} expected An object with the expected properties.
2026
     * @param {Object} actual An object with the actual properties.
2027
     * @param {String} message (Optional) The message to display if the assertion fails.
2028
     * @method propertiesAreEqual
2029
     * @static
2030
     */
2031
    propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/,
2032
                           message /*:String*/) /*:Void*/ {
2033
 
2034
        var Assert = YAHOO.util.Assert;
2035
 
2036
        //get all properties in the object
2037
        var properties /*:Array*/ = [];
2038
        for (var property in expected){
2039
            properties.push(property);
2040
        }
2041
 
2042
        //see if the properties are in the expected object
2043
        for (var i=0; i < properties.length; i++){
2044
            Assert.isNotUndefined(actual[properties[i]],
2045
                Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
2046
        }
2047
 
2048
    },
2049
 
2050
    /**
2051
     * Asserts that an object has a property with the given name.
2052
     * @param {String} propertyName The name of the property to test.
2053
     * @param {Object} object The object to search.
2054
     * @param {String} message (Optional) The message to display if the assertion fails.
2055
     * @method hasProperty
2056
     * @static
2057
     */
2058
    hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2059
        if (!(propertyName in object)){
2060
            var Assert = YAHOO.util.Assert;
2061
            Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
2062
        }
2063
    },
2064
 
2065
    /**
2066
     * Asserts that a property with the given name exists on an object instance (not on its prototype).
2067
     * @param {String} propertyName The name of the property to test.
2068
     * @param {Object} object The object to search.
2069
     * @param {String} message (Optional) The message to display if the assertion fails.
2070
     * @method hasProperty
2071
     * @static
2072
     */
2073
    hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2074
        if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
2075
            var Assert = YAHOO.util.Assert;
2076
            Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
2077
        }
2078
    }
2079
};
2080
//-----------------------------------------------------------------------------
2081
// DateAssert object
2082
//-----------------------------------------------------------------------------
2083
 
2084
/**
2085
 * The DateAssert object provides functions to test JavaScript Date objects
2086
 * for a variety of cases.
2087
 *
2088
 * @namespace YAHOO.util
2089
 * @class DateAssert
2090
 * @static
2091
 */
2092
 
2093
YAHOO.util.DateAssert = {
2094
 
2095
    /**
2096
     * Asserts that a date's month, day, and year are equal to another date's.
2097
     * @param {Date} expected The expected date.
2098
     * @param {Date} actual The actual date to test.
2099
     * @param {String} message (Optional) The message to display if the assertion fails.
2100
     * @method datesAreEqual
2101
     * @static
2102
     */
2103
    datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2104
        if (expected instanceof Date && actual instanceof Date){
2105
            var Assert = YAHOO.util.Assert;
2106
            Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
2107
            Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
2108
            Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
2109
        } else {
2110
            throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
2111
        }
2112
    },
2113
 
2114
    /**
2115
     * Asserts that a date's hour, minutes, and seconds are equal to another date's.
2116
     * @param {Date} expected The expected date.
2117
     * @param {Date} actual The actual date to test.
2118
     * @param {String} message (Optional) The message to display if the assertion fails.
2119
     * @method timesAreEqual
2120
     * @static
2121
     */
2122
    timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2123
        if (expected instanceof Date && actual instanceof Date){
2124
            var Assert = YAHOO.util.Assert;
2125
            Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
2126
            Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
2127
            Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
2128
        } else {
2129
            throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
2130
        }
2131
    }
2132
 
2133
};
2134
YAHOO.namespace("tool");
2135
 
2136
//-----------------------------------------------------------------------------
2137
// TestManager object
2138
//-----------------------------------------------------------------------------
2139
 
2140
/**
2141
 * Runs pages containing test suite definitions.
2142
 * @namespace YAHOO.tool
2143
 * @class TestManager
2144
 * @static
2145
 */
2146
YAHOO.tool.TestManager = {
2147
 
2148
    /**
2149
     * Constant for the testpagebegin custom event
2150
     * @property TEST_PAGE_BEGIN_EVENT
2151
     * @static
2152
     * @type string
2153
     * @final
2154
     */
2155
    TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
2156
 
2157
    /**
2158
     * Constant for the testpagecomplete custom event
2159
     * @property TEST_PAGE_COMPLETE_EVENT
2160
     * @static
2161
     * @type string
2162
     * @final
2163
     */
2164
    TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
2165
 
2166
    /**
2167
     * Constant for the testmanagerbegin custom event
2168
     * @property TEST_MANAGER_BEGIN_EVENT
2169
     * @static
2170
     * @type string
2171
     * @final
2172
     */
2173
    TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
2174
 
2175
    /**
2176
     * Constant for the testmanagercomplete custom event
2177
     * @property TEST_MANAGER_COMPLETE_EVENT
2178
     * @static
2179
     * @type string
2180
     * @final
2181
     */
2182
    TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
2183
 
2184
    //-------------------------------------------------------------------------
2185
    // Private Properties
2186
    //-------------------------------------------------------------------------
2187
 
2188
 
2189
    /**
2190
     * The URL of the page currently being executed.
2191
     * @type String
2192
     * @private
2193
     * @property _curPage
2194
     * @static
2195
     */
2196
    _curPage /*:String*/ : null,
2197
 
2198
    /**
2199
     * The frame used to load and run tests.
2200
     * @type Window
2201
     * @private
2202
     * @property _frame
2203
     * @static
2204
     */
2205
    _frame /*:Window*/ : null,
2206
 
2207
    /**
2208
     * The logger used to output results from the various tests.
2209
     * @type YAHOO.tool.TestLogger
2210
     * @private
2211
     * @property _logger
2212
     * @static
2213
     */
2214
    _logger : null,
2215
 
2216
    /**
2217
     * The timeout ID for the next iteration through the tests.
2218
     * @type int
2219
     * @private
2220
     * @property _timeoutId
2221
     * @static
2222
     */
2223
    _timeoutId /*:int*/ : 0,
2224
 
2225
    /**
2226
     * Array of pages to load.
2227
     * @type String[]
2228
     * @private
2229
     * @property _pages
2230
     * @static
2231
     */
2232
    _pages /*:String[]*/ : [],
2233
 
2234
    /**
2235
     * Aggregated results
2236
     * @type Object
2237
     * @private
2238
     * @property _results
2239
     * @static
2240
     */
2241
    _results: null,
2242
 
2243
    //-------------------------------------------------------------------------
2244
    // Private Methods
2245
    //-------------------------------------------------------------------------
2246
 
2247
    /**
2248
     * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
2249
     * the loop again.
2250
     * @param {Object} data Data about the event.
2251
     * @return {Void}
2252
     * @private
2253
     * @static
2254
     */
2255
    _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
2256
 
2257
        this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT, {
2258
                page: this._curPage,
2259
                results: data.results
2260
            });
2261
 
2262
        //save results
2263
        //this._results[this.curPage] = data.results;
2264
 
2265
        //process 'em
2266
        this._processResults(this._curPage, data.results);
2267
 
2268
        this._logger.clearTestRunner();
2269
 
2270
        //if there's more to do, set a timeout to begin again
2271
        if (this._pages.length){
2272
            this._timeoutId = setTimeout(function(){
2273
                YAHOO.tool.TestManager._run();
2274
            }, 1000);
2275
        } else {
2276
            this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
2277
        }
2278
    },
2279
 
2280
    /**
2281
     * Processes the results of a test page run, outputting log messages
2282
     * for failed tests.
2283
     * @return {Void}
2284
     * @private
2285
     * @static
2286
     */
2287
    _processResults : function (page /*:String*/, results /*:Object*/) /*:Void*/ {
2288
 
2289
        var r = this._results;
2290
 
2291
        r.passed += results.passed;
2292
        r.failed += results.failed;
2293
        r.ignored += results.ignored;
2294
        r.total += results.total;
2295
        r.duration += results.duration;
2296
 
2297
        if (results.failed){
2298
            r.failedPages.push(page);
2299
        } else {
2300
            r.passedPages.push(page);
2301
        }
2302
 
2303
        results.name = page;
2304
        results.type = "page";
2305
 
2306
        r[page] = results;
2307
    },
2308
 
2309
    /**
2310
     * Loads the next test page into the iframe.
2311
     * @return {Void}
2312
     * @static
2313
     * @private
2314
     */
2315
    _run : function () /*:Void*/ {
2316
 
2317
        //set the current page
2318
        this._curPage = this._pages.shift();
2319
 
2320
        this.fireEvent(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
2321
 
2322
        //load the frame - destroy history in case there are other iframes that
2323
        //need testing
2324
        this._frame.location.replace(this._curPage);
2325
 
2326
    },
2327
 
2328
    //-------------------------------------------------------------------------
2329
    // Public Methods
2330
    //-------------------------------------------------------------------------
2331
 
2332
    /**
2333
     * Signals that a test page has been loaded. This should be called from
2334
     * within the test page itself to notify the TestManager that it is ready.
2335
     * @return {Void}
2336
     * @static
2337
     */
2338
    load : function () /*:Void*/ {
2339
        if (parent.YAHOO.tool.TestManager !== this){
2340
            parent.YAHOO.tool.TestManager.load();
2341
        } else {
2342
 
2343
            if (this._frame) {
2344
                //assign event handling
2345
                var TestRunner = this._frame.YAHOO.tool.TestRunner;
2346
 
2347
                this._logger.setTestRunner(TestRunner);
2348
                TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
2349
 
2350
                //run it
2351
                TestRunner.run();
2352
            }
2353
        }
2354
    },
2355
 
2356
    /**
2357
     * Sets the pages to be loaded.
2358
     * @param {String[]} pages An array of URLs to load.
2359
     * @return {Void}
2360
     * @static
2361
     */
2362
    setPages : function (pages /*:String[]*/) /*:Void*/ {
2363
        this._pages = pages;
2364
    },
2365
 
2366
    /**
2367
     * Begins the process of running the tests.
2368
     * @return {Void}
2369
     * @static
2370
     */
2371
    start : function () /*:Void*/ {
2372
 
2373
        if (!this._initialized) {
2374
 
2375
            /**
2376
             * Fires when loading a test page
2377
             * @event testpagebegin
2378
             * @param curPage {string} the page being loaded
2379
             * @static
2380
             */
2381
            this.createEvent(this.TEST_PAGE_BEGIN_EVENT);
2382
 
2383
            /**
2384
             * Fires when a test page is complete
2385
             * @event testpagecomplete
2386
             * @param obj {page: string, results: object} the name of the
2387
             * page that was loaded, and the test suite results
2388
             * @static
2389
             */
2390
            this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);
2391
 
2392
            /**
2393
             * Fires when the test manager starts running all test pages
2394
             * @event testmanagerbegin
2395
             * @static
2396
             */
2397
            this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);
2398
 
2399
            /**
2400
             * Fires when the test manager finishes running all test pages.  External
2401
             * test runners should subscribe to this event in order to get the
2402
             * aggregated test results.
2403
             * @event testmanagercomplete
2404
             * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
2405
             *              tests_failed: int, passed: string[], failed: string[],
2406
             *              page_results: {} }
2407
             * @static
2408
             */
2409
            this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);
2410
 
2411
            //create iframe if not already available
2412
            if (!this._frame){
2413
                var frame /*:HTMLElement*/ = document.createElement("iframe");
2414
                frame.style.visibility = "hidden";
2415
                frame.style.position = "absolute";
2416
                document.body.appendChild(frame);
2417
                this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
2418
            }
2419
 
2420
            //create test logger if not already available
2421
            if (!this._logger){
2422
                this._logger = new YAHOO.tool.TestLogger();
2423
            }
2424
 
2425
            this._initialized = true;
2426
        }
2427
 
2428
 
2429
        // reset the results cache
2430
        this._results = {
2431
 
2432
            passed: 0,
2433
            failed: 0,
2434
            ignored: 0,
2435
            total: 0,
2436
            type: "report",
2437
            name: "YUI Test Results",
2438
            duration: 0,
2439
            failedPages:[],
2440
            passedPages:[]
2441
            /*
2442
            // number of pages that pass
2443
            pages_passed: 0,
2444
            // number of pages that fail
2445
            pages_failed: 0,
2446
            // total number of tests passed
2447
            tests_passed: 0,
2448
            // total number of tests failed
2449
            tests_failed: 0,
2450
            // array of pages that passed
2451
            passed: [],
2452
            // array of pages that failed
2453
            failed: [],
2454
            // map of full results for each page
2455
            page_results: {}*/
2456
        };
2457
 
2458
        this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT, null);
2459
        this._run();
2460
 
2461
    },
2462
 
2463
    /**
2464
     * Stops the execution of tests.
2465
     * @return {Void}
2466
     * @static
2467
     */
2468
    stop : function () /*:Void*/ {
2469
        clearTimeout(this._timeoutId);
2470
    }
2471
 
2472
};
2473
 
2474
YAHOO.lang.augmentObject(YAHOO.tool.TestManager, YAHOO.util.EventProvider.prototype);
2475
 
2476
YAHOO.namespace("tool");
2477
 
2478
//-----------------------------------------------------------------------------
2479
// TestLogger object
2480
//-----------------------------------------------------------------------------
2481
 
2482
/**
2483
 * Displays test execution progress and results, providing filters based on
2484
 * different key events.
2485
 * @namespace YAHOO.tool
2486
 * @class TestLogger
2487
 * @constructor
2488
 * @param {HTMLElement} element (Optional) The element to create the logger in.
2489
 * @param {Object} config (Optional) Configuration options for the logger.
2490
 */
2491
YAHOO.tool.TestLogger = function (element, config) {
2492
    YAHOO.tool.TestLogger.superclass.constructor.call(this, element, config);
2493
    this.init();
2494
};
2495
 
2496
YAHOO.lang.extend(YAHOO.tool.TestLogger, YAHOO.widget.LogReader, {
2497
 
2498
    footerEnabled : true,
2499
    newestOnTop : false,
2500
 
2501
    /**
2502
     * Formats message string to HTML for output to console.
2503
     * @private
2504
     * @method formatMsg
2505
     * @param oLogMsg {Object} Log message object.
2506
     * @return {String} HTML-formatted message for output to console.
2507
     */
2508
    formatMsg : function(message /*:Object*/) {
2509
 
2510
        var category /*:String*/ = message.category;
2511
        var text /*:String*/ = this.html2Text(message.msg);
2512
 
2513
        return "<pre><p><span class=\"" + category + "\">" + category.toUpperCase() + "</span> " + text + "</p></pre>";
2514
 
2515
    },
2516
 
2517
    //-------------------------------------------------------------------------
2518
    // Private Methods
2519
    //-------------------------------------------------------------------------
2520
 
2521
    /*
2522
     * Initializes the logger.
2523
     * @private
2524
     */
2525
    init : function () {
2526
 
2527
        //attach to any available TestRunner
2528
        if (YAHOO.tool.TestRunner){
2529
            this.setTestRunner(YAHOO.tool.TestRunner);
2530
        }
2531
 
2532
        //hide useless sources
2533
        this.hideSource("global");
2534
        this.hideSource("LogReader");
2535
 
2536
        //hide useless message categories
2537
        this.hideCategory("warn");
2538
        this.hideCategory("window");
2539
        this.hideCategory("time");
2540
 
2541
        //reset the logger
2542
        this.clearConsole();
2543
    },
2544
 
2545
    /**
2546
     * Clears the reference to the TestRunner from previous operations. This
2547
     * unsubscribes all events and removes the object reference.
2548
     * @return {Void}
2549
     * @static
2550
     */
2551
    clearTestRunner : function () /*:Void*/ {
2552
        if (this._runner){
2553
            this._runner.unsubscribeAll();
2554
            this._runner = null;
2555
        }
2556
    },
2557
 
2558
    /**
2559
     * Sets the source test runner that the logger should monitor.
2560
     * @param {YAHOO.tool.TestRunner} testRunner The TestRunner to observe.
2561
     * @return {Void}
2562
     * @static
2563
     */
2564
    setTestRunner : function (testRunner /*:YAHOO.tool.TestRunner*/) /*:Void*/ {
2565
 
2566
        if (this._runner){
2567
            this.clearTestRunner();
2568
        }
2569
 
2570
        this._runner = testRunner;
2571
 
2572
        //setup event _handlers
2573
        testRunner.subscribe(testRunner.TEST_PASS_EVENT, this._handleTestRunnerEvent, this, true);
2574
        testRunner.subscribe(testRunner.TEST_FAIL_EVENT, this._handleTestRunnerEvent, this, true);
2575
        testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, this._handleTestRunnerEvent, this, true);
2576
        testRunner.subscribe(testRunner.BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2577
        testRunner.subscribe(testRunner.COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2578
        testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2579
        testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2580
        testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2581
        testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2582
    },
2583
 
2584
    //-------------------------------------------------------------------------
2585
    // Event Handlers
2586
    //-------------------------------------------------------------------------
2587
 
2588
    /**
2589
     * Handles all TestRunner events, outputting appropriate data into the console.
2590
     * @param {Object} data The event data object.
2591
     * @return {Void}
2592
     * @private
2593
     */
2594
    _handleTestRunnerEvent : function (data /*:Object*/) /*:Void*/ {
2595
 
2596
        //shortcut variables
2597
        var TestRunner /*:Object*/ = YAHOO.tool.TestRunner;
2598
 
2599
        //data variables
2600
        var message /*:String*/ = "";
2601
        var messageType /*:String*/ = "";
2602
 
2603
        switch(data.type){
2604
            case TestRunner.BEGIN_EVENT:
2605
                message = "Testing began at " + (new Date()).toString() + ".";
2606
                messageType = "info";
2607
                break;
2608
 
2609
            case TestRunner.COMPLETE_EVENT:
2610
                message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" +
2611
                    data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2612
                messageType = "info";
2613
                break;
2614
 
2615
            case TestRunner.TEST_FAIL_EVENT:
2616
                message = data.testName + ": " + data.error.getMessage();
2617
                messageType = "fail";
2618
                break;
2619
 
2620
            case TestRunner.TEST_IGNORE_EVENT:
2621
                message = data.testName + ": ignored.";
2622
                messageType = "ignore";
2623
                break;
2624
 
2625
            case TestRunner.TEST_PASS_EVENT:
2626
                message = data.testName + ": passed.";
2627
                messageType = "pass";
2628
                break;
2629
 
2630
            case TestRunner.TEST_SUITE_BEGIN_EVENT:
2631
                message = "Test suite \"" + data.testSuite.name + "\" started.";
2632
                messageType = "info";
2633
                break;
2634
 
2635
            case TestRunner.TEST_SUITE_COMPLETE_EVENT:
2636
                message = "Test suite \"" + data.testSuite.name + "\" completed.\nPassed:" +
2637
                    data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2638
                messageType = "info";
2639
                break;
2640
 
2641
            case TestRunner.TEST_CASE_BEGIN_EVENT:
2642
                message = "Test case \"" + data.testCase.name + "\" started.";
2643
                messageType = "info";
2644
                break;
2645
 
2646
            case TestRunner.TEST_CASE_COMPLETE_EVENT:
2647
                message = "Test case \"" + data.testCase.name + "\" completed.\nPassed:" +
2648
                    data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2649
                messageType = "info";
2650
                break;
2651
            default:
2652
                message = "Unexpected event " + data.type;
2653
                message = "info";
2654
        }
2655
 
2656
        YAHOO.log(message, messageType, "TestRunner");
2657
    }
2658
 
2659
});
2660
YAHOO.namespace("tool.TestFormat");
2661
 
2662
(function(){
2663
 
2664
    /**
2665
     * Returns test results formatted as a JSON string. Requires JSON utility.
2666
     * @param {Object} result The results object created by TestRunner.
2667
     * @return {String} An XML-formatted string of results.
2668
     * @namespace YAHOO.tool.TestFormat
2669
     * @method JSON
2670
     * @static
2671
     */
2672
    YAHOO.tool.TestFormat.JSON = function(results) {
2673
        return YAHOO.lang.JSON.stringify(results);
2674
    };
2675
 
2676
    /* (intentionally not documented)
2677
     * Simple escape function for XML attribute values.
2678
     * @param {String} text The text to escape.
2679
     * @return {String} The escaped text.
2680
     */
2681
    function xmlEscape(text){
2682
        return text.replace(/["'<>&]/g, function(c){
2683
            switch(c){
2684
                case "<":   return "&lt;";
2685
                case ">":   return "&gt;";
2686
                case "\"":  return "&quot;";
2687
                case "'":   return "&apos;";
2688
                case "&":   return "&amp;";
2689
            }
2690
        });
2691
    }
2692
 
2693
    /**
2694
     * Returns test results formatted as an XML string.
2695
     * @param {Object} result The results object created by TestRunner.
2696
     * @return {String} An XML-formatted string of results.
2697
     * @namespace YAHOO.tool.TestFormat
2698
     * @method XML
2699
     * @static
2700
     */
2701
    YAHOO.tool.TestFormat.XML = function(results) {
2702
 
2703
        function serializeToXML(results){
2704
            var l   = YAHOO.lang,
2705
                xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
2706
 
2707
            if (l.isNumber(results.duration)){
2708
                xml += " duration=\"" + results.duration + "\"";
2709
            }
2710
 
2711
            if (results.type == "test"){
2712
                xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
2713
            } else {
2714
                xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2715
                for (var prop in results) {
2716
                    if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2717
                        xml += serializeToXML(results[prop]);
2718
                    }
2719
                }
2720
            }
2721
 
2722
            xml += "</" + results.type + ">";
2723
 
2724
            return xml;
2725
        }
2726
 
2727
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
2728
 
2729
    };
2730
 
2731
 
2732
    /**
2733
     * Returns test results formatted in JUnit XML format.
2734
     * @param {Object} result The results object created by TestRunner.
2735
     * @return {String} An XML-formatted string of results.
2736
     * @namespace YAHOO.tool.TestFormat
2737
     * @method JUnitXML
2738
     * @static
2739
     */
2740
    YAHOO.tool.TestFormat.JUnitXML = function(results) {
2741
 
2742
 
2743
        function serializeToJUnitXML(results){
2744
            var l   = YAHOO.lang,
2745
                xml = "",
2746
                prop;
2747
 
2748
            switch (results.type){
2749
                //equivalent to testcase in JUnit
2750
                case "test":
2751
                    if (results.result != "ignore"){
2752
                        xml = "<testcase name=\"" + xmlEscape(results.name) + "\">";
2753
                        if (results.result == "fail"){
2754
                            xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
2755
                        }
2756
                        xml+= "</testcase>";
2757
                    }
2758
                    break;
2759
 
2760
                //equivalent to testsuite in JUnit
2761
                case "testcase":
2762
 
2763
                    xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\">";
2764
 
2765
                    for (prop in results) {
2766
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2767
                            xml += serializeToJUnitXML(results[prop]);
2768
                        }
2769
                    }
2770
 
2771
                    xml += "</testsuite>";
2772
                    break;
2773
 
2774
                case "testsuite":
2775
                    for (prop in results) {
2776
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2777
                            xml += serializeToJUnitXML(results[prop]);
2778
                        }
2779
                    }
2780
 
2781
                    //skip output - no JUnit equivalent
2782
                    break;
2783
 
2784
                case "report":
2785
 
2786
                    xml = "<testsuites>";
2787
 
2788
                    for (prop in results) {
2789
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2790
                            xml += serializeToJUnitXML(results[prop]);
2791
                        }
2792
                    }
2793
 
2794
                    xml += "</testsuites>";
2795
 
2796
                //no default
2797
            }
2798
 
2799
            return xml;
2800
 
2801
        }
2802
 
2803
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
2804
    };
2805
 
2806
    /**
2807
     * Returns test results formatted in TAP format.
2808
     * For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
2809
     * @param {Object} result The results object created by TestRunner.
2810
     * @return {String} A TAP-formatted string of results.
2811
     * @namespace YAHOO.tool.TestFormat
2812
     * @method TAP
2813
     * @static
2814
     */
2815
    YAHOO.tool.TestFormat.TAP = function(results) {
2816
 
2817
        var currentTestNum = 1;
2818
 
2819
        function serializeToTAP(results){
2820
            var l   = YAHOO.lang,
2821
                text = "";
2822
 
2823
            switch (results.type){
2824
 
2825
                case "test":
2826
                    if (results.result != "ignore"){
2827
 
2828
                        text = "ok " + (currentTestNum++) + " - " + results.name;
2829
 
2830
                        if (results.result == "fail"){
2831
                            text = "not " + text + " - " + results.message;
2832
                        }
2833
 
2834
                        text += "\n";
2835
                    } else {
2836
                        text = "#Ignored test " + results.name + "\n";
2837
                    }
2838
                    break;
2839
 
2840
                case "testcase":
2841
 
2842
                    text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2843
 
2844
 
2845
                    for (prop in results) {
2846
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2847
                            text += serializeToTAP(results[prop]);
2848
                        }
2849
                    }
2850
 
2851
                    text += "#End testcase " + results.name + "\n";
2852
 
2853
 
2854
                    break;
2855
 
2856
                case "testsuite":
2857
 
2858
                    text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2859
 
2860
                    for (prop in results) {
2861
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2862
                            text += serializeToTAP(results[prop]);
2863
                        }
2864
                    }
2865
 
2866
                    text += "#End testsuite " + results.name + "\n";
2867
                    break;
2868
 
2869
                case "report":
2870
 
2871
                    for (prop in results) {
2872
                        if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2873
                            text += serializeToTAP(results[prop]);
2874
                        }
2875
                    }
2876
 
2877
                //no default
2878
            }
2879
 
2880
            return text;
2881
 
2882
        }
2883
 
2884
        return "1.." + results.total + "\n" + serializeToTAP(results);
2885
    };
2886
 
2887
})();
2888
YAHOO.namespace("tool.CoverageFormat");
2889
 
2890
/**
2891
 * Returns the coverage report in JSON format. This is the straight
2892
 * JSON representation of the native coverage report.
2893
 * @param {Object} coverage The coverage report object.
2894
 * @return {String} A JSON-formatted string of coverage data.
2895
 * @method JSON
2896
 * @namespace YAHOO.tool.CoverageFormat
2897
 */
2898
YAHOO.tool.CoverageFormat.JSON = function(coverage){
2899
    return YAHOO.lang.JSON.stringify(coverage);
2900
};
2901
 
2902
/**
2903
 * Returns the coverage report in a JSON format compatible with
2904
 * Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
2905
 * for more information. Note: function coverage is not available
2906
 * in this format.
2907
 * @param {Object} coverage The coverage report object.
2908
 * @return {String} A JSON-formatted string of coverage data.
2909
 * @method XdebugJSON
2910
 * @namespace YAHOO.tool.CoverageFormat
2911
 */
2912
YAHOO.tool.CoverageFormat.XdebugJSON = function(coverage){
2913
    var report = {},
2914
        prop;
2915
    for (prop in coverage){
2916
        if (coverage.hasOwnProperty(prop)){
2917
            report[prop] = coverage[prop].lines;
2918
        }
2919
    }
2920
 
2921
    return YAHOO.lang.JSON.stringify(report);
2922
};
2923
 
2924
YAHOO.namespace("tool");
2925
 
2926
/**
2927
 * An object capable of sending test results to a server.
2928
 * @param {String} url The URL to submit the results to.
2929
 * @param {Function} format (Optiona) A function that outputs the results in a specific format.
2930
 *      Default is YAHOO.tool.TestFormat.XML.
2931
 * @constructor
2932
 * @namespace YAHOO.tool
2933
 * @class TestReporter
2934
 */
2935
YAHOO.tool.TestReporter = function(url /*:String*/, format /*:Function*/) {
2936
 
2937
    /**
2938
     * The URL to submit the data to.
2939
     * @type String
2940
     * @property url
2941
     */
2942
    this.url /*:String*/ = url;
2943
 
2944
    /**
2945
     * The formatting function to call when submitting the data.
2946
     * @type Function
2947
     * @property format
2948
     */
2949
    this.format /*:Function*/ = format || YAHOO.tool.TestFormat.XML;
2950
 
2951
    /**
2952
     * Extra fields to submit with the request.
2953
     * @type Object
2954
     * @property _fields
2955
     * @private
2956
     */
2957
    this._fields /*:Object*/ = new Object();
2958
 
2959
    /**
2960
     * The form element used to submit the results.
2961
     * @type HTMLFormElement
2962
     * @property _form
2963
     * @private
2964
     */
2965
    this._form /*:HTMLElement*/ = null;
2966
 
2967
    /**
2968
     * Iframe used as a target for form submission.
2969
     * @type HTMLIFrameElement
2970
     * @property _iframe
2971
     * @private
2972
     */
2973
    this._iframe /*:HTMLElement*/ = null;
2974
};
2975
 
2976
YAHOO.tool.TestReporter.prototype = {
2977
 
2978
    //restore missing constructor
2979
    constructor: YAHOO.tool.TestReporter,
2980
 
2981
    /**
2982
     * Convert a date into ISO format.
2983
     * From Douglas Crockford's json2.js
2984
     * @param {Date} date The date to convert.
2985
     * @return {String} An ISO-formatted date string
2986
     * @method _convertToISOString
2987
     * @private
2988
     */
2989
    _convertToISOString: function(date){
2990
        function f(n) {
2991
            // Format integers to have at least two digits.
2992
            return n < 10 ? '0' + n : n;
2993
        }
2994
 
2995
        return date.getUTCFullYear()   + '-' +
2996
             f(date.getUTCMonth() + 1) + '-' +
2997
             f(date.getUTCDate())      + 'T' +
2998
             f(date.getUTCHours())     + ':' +
2999
             f(date.getUTCMinutes())   + ':' +
3000
             f(date.getUTCSeconds())   + 'Z';
3001
 
3002
    },
3003
 
3004
    /**
3005
     * Adds a field to the form that submits the results.
3006
     * @param {String} name The name of the field.
3007
     * @param {Variant} value The value of the field.
3008
     * @return {Void}
3009
     * @method addField
3010
     */
3011
    addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
3012
        this._fields[name] = value;
3013
    },
3014
 
3015
    /**
3016
     * Removes all previous defined fields.
3017
     * @return {Void}
3018
     * @method addField
3019
     */
3020
    clearFields : function() /*:Void*/{
3021
        this._fields = new Object();
3022
    },
3023
 
3024
    /**
3025
     * Cleans up the memory associated with the TestReporter, removing DOM elements
3026
     * that were created.
3027
     * @return {Void}
3028
     * @method destroy
3029
     */
3030
    destroy : function() /*:Void*/ {
3031
        if (this._form){
3032
            this._form.parentNode.removeChild(this._form);
3033
            this._form = null;
3034
        }
3035
        if (this._iframe){
3036
            this._iframe.parentNode.removeChild(this._iframe);
3037
            this._iframe = null;
3038
        }
3039
        this._fields = null;
3040
    },
3041
 
3042
    /**
3043
     * Sends the report to the server.
3044
     * @param {Object} results The results object created by TestRunner.
3045
     * @return {Void}
3046
     * @method report
3047
     */
3048
    report : function(results /*:Object*/) /*:Void*/{
3049
 
3050
        //if the form hasn't been created yet, create it
3051
        if (!this._form){
3052
            this._form = document.createElement("form");
3053
            this._form.method = "post";
3054
            this._form.style.visibility = "hidden";
3055
            this._form.style.position = "absolute";
3056
            this._form.style.top = 0;
3057
            document.body.appendChild(this._form);
3058
 
3059
            //IE won't let you assign a name using the DOM, must do it the hacky way
3060
            if (YAHOO.env.ua.ie){
3061
                this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
3062
            } else {
3063
                this._iframe = document.createElement("iframe");
3064
                this._iframe.name = "yuiTestTarget";
3065
            }
3066
 
3067
            this._iframe.src = "javascript:false";
3068
            this._iframe.style.visibility = "hidden";
3069
            this._iframe.style.position = "absolute";
3070
            this._iframe.style.top = 0;
3071
            document.body.appendChild(this._iframe);
3072
 
3073
            this._form.target = "yuiTestTarget";
3074
        }
3075
 
3076
        //set the form's action
3077
        this._form.action = this.url;
3078
 
3079
        //remove any existing fields
3080
        while(this._form.hasChildNodes()){
3081
            this._form.removeChild(this._form.lastChild);
3082
        }
3083
 
3084
        //create default fields
3085
        this._fields.results = this.format(results);
3086
        this._fields.useragent = navigator.userAgent;
3087
        this._fields.timestamp = this._convertToISOString(new Date());
3088
 
3089
        //add fields to the form
3090
        for (var prop in this._fields){
3091
            if (YAHOO.lang.hasOwnProperty(this._fields, prop) && typeof this._fields[prop] != "function"){
3092
                if (YAHOO.env.ua.ie){
3093
                    input = document.createElement("<input name=\"" + prop + "\" >");
3094
                } else {
3095
                    input = document.createElement("input");
3096
                    input.name = prop;
3097
                }
3098
                input.type = "hidden";
3099
                input.value = this._fields[prop];
3100
                this._form.appendChild(input);
3101
            }
3102
        }
3103
 
3104
        //remove default fields
3105
        delete this._fields.results;
3106
        delete this._fields.useragent;
3107
        delete this._fields.timestamp;
3108
 
3109
        if (arguments[1] !== false){
3110
            this._form.submit();
3111
        }
3112
 
3113
    }
3114
 
3115
};
3116
/*Stub for future compatibility*/
3117
YUITest = {
3118
    TestRunner:     YAHOO.tool.TestRunner,
3119
    ResultsFormat:  YAHOO.tool.TestFormat,
3120
    CoverageFormat: YAHOO.tool.CoverageFormat
3121
};
3122
YAHOO.register("yuitest", YAHOO.tool.TestRunner, {version: "2.9.0", build: "2800"});
3123
 
3124
}, '2.9.0' ,{"requires": ["yui2-skin-sam-yuitest", "yui2-yahoo", "yui2-dom", "yui2-event", "yui2-skin-sam-logger", "yui2-logger"], "optional": ["yui2-event-simulate"]});