/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk
261 by Martin Albisetti
Upgrade YUI3 to PR2
1
/*
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5
version: 3.0.0pr2
6
*/
7
YUI.add('yuitest', function(Y) {
8
9
    /**
10
     * YUI JavaScript Testing Framework
11
     *
12
     * @module yuitest
13
     */
14
15
    
16
    Y.namespace("Test");
17
    
18
    /**
19
     * Test case containing various tests to run.
20
     * @param template An object containing any number of test methods, other methods,
21
     *                 an optional name, and anything else the test case needs.
22
     * @class Case
23
     * @namespace Test
24
     * @constructor
25
     */
26
    Y.Test.Case = function (template /*:Object*/) {
27
        
28
        /**
29
         * Special rules for the test case. Possible subobjects
30
         * are fail, for tests that should fail, and error, for
31
         * tests that should throw an error.
32
         */
33
        this._should /*:Object*/ = {};
34
        
35
        //copy over all properties from the template to this object
36
        for (var prop in template) {
37
            this[prop] = template[prop];
38
        }    
39
        
40
        //check for a valid name
41
        if (!Y.Lang.isString(this.name)){
42
            /**
43
             * Name for the test case.
44
             */
45
            this.name /*:String*/ = "testCase" + Y.guid();
46
        }
47
    
48
    };
49
            
50
    Y.Test.Case.prototype = {  
51
    
52
        /**
53
         * Resumes a paused test and runs the given function.
54
         * @param {Function} segment (Optional) The function to run.
55
         *      If omitted, the test automatically passes.
56
         * @return {Void}
57
         * @method resume
58
         */
59
        resume : function (segment /*:Function*/) /*:Void*/ {
60
            Y.Test.Runner.resume(segment);
61
        },
62
    
63
        /**
64
         * Causes the test case to wait a specified amount of time and then
65
         * continue executing the given code.
66
         * @param {Function} segment (Optional) The function to run after the delay.
67
         *      If omitted, the TestRunner will wait until resume() is called.
68
         * @param {int} delay (Optional) The number of milliseconds to wait before running
69
         *      the function. If omitted, defaults to zero.
70
         * @return {Void}
71
         * @method wait
72
         */
73
        wait : function (segment /*:Function*/, delay /*:int*/) /*:Void*/{
74
            var args = arguments;
75
            if (Y.Lang.isFunction(args[0])){
76
                throw new Y.Test.Wait(args[0], args[1]);
77
            } else {
78
                throw new Y.Test.Wait(function(){
79
                    Y.Assert.fail("Timeout: wait() called but resume() never called.");
80
                }, (Y.Lang.isNumber(args[0]) ? args[0] : 10000));
81
            }
82
        },
83
    
84
        //-------------------------------------------------------------------------
85
        // Stub Methods
86
        //-------------------------------------------------------------------------
87
    
88
        /**
89
         * Function to run before each test is executed.
90
         * @return {Void}
91
         * @method setUp
92
         */
93
        setUp : function () /*:Void*/ {
94
        },
95
        
96
        /**
97
         * Function to run after each test is executed.
98
         * @return {Void}
99
         * @method tearDown
100
         */
101
        tearDown: function () /*:Void*/ {    
102
        }
103
    };
104
    
105
    /**
106
     * Represents a stoppage in test execution to wait for an amount of time before
107
     * continuing.
108
     * @param {Function} segment A function to run when the wait is over.
109
     * @param {int} delay The number of milliseconds to wait before running the code.
110
     * @class Wait
111
     * @namespace Test
112
     * @constructor
113
     *
114
     */
115
    Y.Test.Wait = function (segment /*:Function*/, delay /*:int*/) {
116
        
117
        /**
118
         * The segment of code to run when the wait is over.
119
         * @type Function
120
         * @property segment
121
         */
122
        this.segment /*:Function*/ = (Y.Lang.isFunction(segment) ? segment : null);
123
    
124
        /**
125
         * The delay before running the segment of code.
126
         * @type int
127
         * @property delay
128
         */
129
        this.delay /*:int*/ = (Y.Lang.isNumber(delay) ? delay : 0);        
130
    };
131
132
133
        
134
    Y.namespace("Test");
135
    
136
    /**
137
     * A test suite that can contain a collection of TestCase and TestSuite objects.
138
     * @param {String||Object} data The name of the test suite or an object containing
139
     *      a name property as well as setUp and tearDown methods.
140
     * @namespace Test
141
     * @class Suite
142
     * @constructor
143
     */
144
    Y.Test.Suite = function (data /*:String||Object*/) {
145
    
146
        /**
147
         * The name of the test suite.
148
         * @type String
149
         * @property name
150
         */
151
        this.name /*:String*/ = "";
152
    
153
        /**
154
         * Array of test suites and
155
         * @private
156
         */
157
        this.items /*:Array*/ = [];
158
    
159
        //initialize the properties
160
        if (Y.Lang.isString(data)){
161
            this.name = data;
162
        } else if (Y.Lang.isObject(data)){
163
            Y.mix(this, data, true);
164
        }
165
    
166
        //double-check name
167
        if (this.name === ""){
168
            this.name = "testSuite" + Y.guid();
169
        }
170
    
171
    };
172
    
173
    Y.Test.Suite.prototype = {
174
        
175
        /**
176
         * Adds a test suite or test case to the test suite.
177
         * @param {Y.Test.Suite||Y.Test.Case} testObject The test suite or test case to add.
178
         * @return {Void}
179
         * @method add
180
         */
181
        add : function (testObject /*:Y.Test.Suite*/) /*:Void*/ {
182
            if (testObject instanceof Y.Test.Suite || testObject instanceof Y.Test.Case) {
183
                this.items.push(testObject);
184
            }
185
        },
186
        
187
        //-------------------------------------------------------------------------
188
        // Stub Methods
189
        //-------------------------------------------------------------------------
190
    
191
        /**
192
         * Function to run before each test is executed.
193
         * @return {Void}
194
         * @method setUp
195
         */
196
        setUp : function () /*:Void*/ {
197
        },
198
        
199
        /**
200
         * Function to run after each test is executed.
201
         * @return {Void}
202
         * @method tearDown
203
         */
204
        tearDown: function () /*:Void*/ {
205
        }
206
        
207
    };
208
209
    
210
    /*
211
     * Runs test suites and test cases, providing events to allowing for the
212
     * interpretation of test results.
213
     * @namespace Test
214
     * @class Runner
215
     * @static
216
     */
217
    Y.Test.Runner = (function(){
218
    
219
        /**
220
         * A node in the test tree structure. May represent a TestSuite, TestCase, or
221
         * test function.
222
         * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
223
         * @class TestNode
224
         * @constructor
225
         * @private
226
         */
227
        function TestNode(testObject /*:Variant*/){
228
        
229
            /**
230
             * The TestSuite, TestCase, or test function represented by this node.
231
             * @type Variant
232
             * @property testObject
233
             */
234
            this.testObject = testObject;
235
            
236
            /**
237
             * Pointer to this node's first child.
238
             * @type TestNode
239
             * @property firstChild
240
             */        
241
            this.firstChild /*:TestNode*/ = null;
242
            
243
            /**
244
             * Pointer to this node's last child.
245
             * @type TestNode
246
             * @property lastChild
247
             */        
248
            this.lastChild = null;
249
            
250
            /**
251
             * Pointer to this node's parent.
252
             * @type TestNode
253
             * @property parent
254
             */        
255
            this.parent = null; 
256
       
257
            /**
258
             * Pointer to this node's next sibling.
259
             * @type TestNode
260
             * @property next
261
             */        
262
            this.next = null;
263
            
264
            /**
265
             * Test results for this test object.
266
             * @type object
267
             * @property results
268
             */                
269
            this.results /*:Object*/ = {
270
                passed : 0,
271
                failed : 0,
272
                total : 0,
273
                ignored : 0
274
            };
275
            
276
            //initialize results
277
            if (testObject instanceof Y.Test.Suite){
278
                this.results.type = "testsuite";
279
                this.results.name = testObject.name;
280
            } else if (testObject instanceof Y.Test.Case){
281
                this.results.type = "testcase";
282
                this.results.name = testObject.name;
283
            }
284
           
285
        }
286
        
287
        TestNode.prototype = {
288
        
289
            /**
290
             * Appends a new test object (TestSuite, TestCase, or test function name) as a child
291
             * of this node.
292
             * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
293
             * @return {Void}
294
             */
295
            appendChild : function (testObject /*:Variant*/) /*:Void*/{
296
                var node = new TestNode(testObject);
297
                if (this.firstChild === null){
298
                    this.firstChild = this.lastChild = node;
299
                } else {
300
                    this.lastChild.next = node;
301
                    this.lastChild = node;
302
                }
303
                node.parent = this;
304
                return node;
305
            }       
306
        };
307
    
308
        /**
309
         * Runs test suites and test cases, providing events to allowing for the
310
         * interpretation of test results.
311
         * @namespace Test
312
         * @class Runner
313
         * @static
314
         */
315
        function TestRunner(){
316
        
317
            //inherit from EventProvider
318
            TestRunner.superclass.constructor.apply(this,arguments);
319
            
320
            /**
321
             * Suite on which to attach all TestSuites and TestCases to be run.
322
             * @type Y.Test.Suite
323
             * @property masterSuite
324
             * @static
325
             * @private
326
             */
327
            this.masterSuite /*:Y.Test.Suite*/ = new Y.Test.Suite("YUI Test Results");        
328
    
329
            /**
330
             * Pointer to the current node in the test tree.
331
             * @type TestNode
332
             * @private
333
             * @property _cur
334
             * @static
335
             */
336
            this._cur = null;
337
            
338
            /**
339
             * Pointer to the root node in the test tree.
340
             * @type TestNode
341
             * @private
342
             * @property _root
343
             * @static
344
             */
345
            this._root = null;
346
            
347
            /**
348
             * Indicates if the TestRunner will log events or not.
349
             * @type Boolean
350
             * @property _log
351
             * @private
352
             * @static
353
             */
354
            this._log = true;
355
            
356
            //create events
357
            var events = [
358
                this.TEST_CASE_BEGIN_EVENT,
359
                this.TEST_CASE_COMPLETE_EVENT,
360
                this.TEST_SUITE_BEGIN_EVENT,
361
                this.TEST_SUITE_COMPLETE_EVENT,
362
                this.TEST_PASS_EVENT,
363
                this.TEST_FAIL_EVENT,
364
                this.TEST_IGNORE_EVENT,
365
                this.COMPLETE_EVENT,
366
                this.BEGIN_EVENT
367
            ];
368
            for (var i=0; i < events.length; i++){
369
                this.subscribe(events[i], this._logEvent, this, true);
370
            }      
371
       
372
        }
373
        
374
        Y.extend(TestRunner, Y.Event.Target, {
375
        
376
            //-------------------------------------------------------------------------
377
            // Constants
378
            //-------------------------------------------------------------------------
379
             
380
            /**
381
             * Fires when a test case is opened but before the first 
382
             * test is executed.
383
             * @event testcasebegin
384
             * @static
385
             */         
386
            TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
387
            
388
            /**
389
             * Fires when all tests in a test case have been executed.
390
             * @event testcasecomplete
391
             * @static
392
             */        
393
            TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
394
            
395
            /**
396
             * Fires when a test suite is opened but before the first 
397
             * test is executed.
398
             * @event testsuitebegin
399
             * @static
400
             */        
401
            TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
402
            
403
            /**
404
             * Fires when all test cases in a test suite have been
405
             * completed.
406
             * @event testsuitecomplete
407
             * @static
408
             */        
409
            TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
410
            
411
            /**
412
             * Fires when a test has passed.
413
             * @event pass
414
             * @static
415
             */        
416
            TEST_PASS_EVENT /*:String*/ : "pass",
417
            
418
            /**
419
             * Fires when a test has failed.
420
             * @event fail
421
             * @static
422
             */        
423
            TEST_FAIL_EVENT /*:String*/ : "fail",
424
            
425
            /**
426
             * Fires when a test has been ignored.
427
             * @event ignore
428
             * @static
429
             */        
430
            TEST_IGNORE_EVENT /*:String*/ : "ignore",
431
            
432
            /**
433
             * Fires when all test suites and test cases have been completed.
434
             * @event complete
435
             * @static
436
             */        
437
            COMPLETE_EVENT /*:String*/ : "complete",
438
            
439
            /**
440
             * Fires when the run() method is called.
441
             * @event begin
442
             * @static
443
             */        
444
            BEGIN_EVENT /*:String*/ : "begin",    
445
            
446
            //-------------------------------------------------------------------------
447
            // Logging-Related Methods
448
            //-------------------------------------------------------------------------
449
    
450
            
451
            /**
452
             * Disable logging via Y.log(). Test output will not be visible unless
453
             * TestRunner events are subscribed to.
454
             * @return {Void}
455
             * @method disableLogging
456
             * @static
457
             */
458
            disableLogging: function(){
459
                this._log = false;
460
            },    
461
            
462
            /**
463
             * Enable logging via Y.log(). Test output is published and can be read via
464
             * logreader.
465
             * @return {Void}
466
             * @method enableLogging
467
             * @static
468
             */
469
            enableLogging: function(){
470
                this._log = true;
471
            },
472
            
473
            /**
474
             * Logs TestRunner events using Y.log().
475
             * @param {Object} event The event object for the event.
476
             * @return {Void}
477
             * @method _logEvent
478
             * @private
479
             * @static
480
             */
481
            _logEvent: function(event){
482
                
483
                //data variables
484
                var message /*:String*/ = "";
485
                var messageType /*:String*/ = "";
486
                
487
                switch(event.type){
488
                    case this.BEGIN_EVENT:
489
                        message = "Testing began at " + (new Date()).toString() + ".";
490
                        messageType = "info";
491
                        break;
492
                        
493
                    case this.COMPLETE_EVENT:
494
                        message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" + 
495
                            event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
496
                        messageType = "info";
497
                        break;
498
                        
499
                    case this.TEST_FAIL_EVENT:
500
                        message = event.testName + ": " + event.error.getMessage();
501
                        messageType = "fail";
502
                        break;
503
                        
504
                    case this.TEST_IGNORE_EVENT:
505
                        message = event.testName + ": ignored.";
506
                        messageType = "ignore";
507
                        break;
508
                        
509
                    case this.TEST_PASS_EVENT:
510
                        message = event.testName + ": passed.";
511
                        messageType = "pass";
512
                        break;
513
                        
514
                    case this.TEST_SUITE_BEGIN_EVENT:
515
                        message = "Test suite \"" + event.testSuite.name + "\" started.";
516
                        messageType = "info";
517
                        break;
518
                        
519
                    case this.TEST_SUITE_COMPLETE_EVENT:
520
                        message = "Test suite \"" + event.testSuite.name + "\" completed.\nPassed:" + 
521
                            event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
522
                        messageType = "info";
523
                        break;
524
                        
525
                    case this.TEST_CASE_BEGIN_EVENT:
526
                        message = "Test case \"" + event.testCase.name + "\" started.";
527
                        messageType = "info";
528
                        break;
529
                        
530
                    case this.TEST_CASE_COMPLETE_EVENT:
531
                        message = "Test case \"" + event.testCase.name + "\" completed.\nPassed:" + 
532
                            event.results.passed + " Failed:" + event.results.failed + " Total:" + event.results.total;
533
                        messageType = "info";
534
                        break;
535
                    default:
536
                        message = "Unexpected event " + event.type;
537
                        message = "info";
538
                }
539
            
540
                //only log if required
541
                if (this._log){
542
                    Y.log(message, messageType, "TestRunner");
543
                }
544
            },
545
546
            //-------------------------------------------------------------------------
547
            // Test Tree-Related Methods
548
            //-------------------------------------------------------------------------
549
    
550
            /**
551
             * Adds a test case to the test tree as a child of the specified node.
552
             * @param {TestNode} parentNode The node to add the test case to as a child.
553
             * @param {Y.Test.Case} testCase The test case to add.
554
             * @return {Void}
555
             * @static
556
             * @private
557
             * @method _addTestCaseToTestTree
558
             */
559
           _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:Y.Test.Case*/) /*:Void*/{
560
                
561
                //add the test suite
562
                var node = parentNode.appendChild(testCase);
563
                
564
                //iterate over the items in the test case
565
                for (var prop in testCase){
566
                    if (prop.indexOf("test") === 0 && Y.Lang.isFunction(testCase[prop])){
567
                        node.appendChild(prop);
568
                    }
569
                }
570
             
571
            },
572
            
573
            /**
574
             * Adds a test suite to the test tree as a child of the specified node.
575
             * @param {TestNode} parentNode The node to add the test suite to as a child.
576
             * @param {Y.Test.Suite} testSuite The test suite to add.
577
             * @return {Void}
578
             * @static
579
             * @private
580
             * @method _addTestSuiteToTestTree
581
             */
582
            _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:Y.Test.Suite*/) /*:Void*/ {
583
                
584
                //add the test suite
585
                var node = parentNode.appendChild(testSuite);
586
                
587
                //iterate over the items in the master suite
588
                for (var i=0; i < testSuite.items.length; i++){
589
                    if (testSuite.items[i] instanceof Y.Test.Suite) {
590
                        this._addTestSuiteToTestTree(node, testSuite.items[i]);
591
                    } else if (testSuite.items[i] instanceof Y.Test.Case) {
592
                        this._addTestCaseToTestTree(node, testSuite.items[i]);
593
                    }                   
594
                }            
595
            },
596
            
597
            /**
598
             * Builds the test tree based on items in the master suite. The tree is a hierarchical
599
             * representation of the test suites, test cases, and test functions. The resulting tree
600
             * is stored in _root and the pointer _cur is set to the root initially.
601
             * @return {Void}
602
             * @static
603
             * @private
604
             * @method _buildTestTree
605
             */
606
            _buildTestTree : function () /*:Void*/ {
607
            
608
                this._root = new TestNode(this.masterSuite);
609
                this._cur = this._root;
610
                
611
                //iterate over the items in the master suite
612
                for (var i=0; i < this.masterSuite.items.length; i++){
613
                    if (this.masterSuite.items[i] instanceof Y.Test.Suite) {
614
                        this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
615
                    } else if (this.masterSuite.items[i] instanceof Y.Test.Case) {
616
                        this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
617
                    }                   
618
                }            
619
            
620
            }, 
621
        
622
            //-------------------------------------------------------------------------
623
            // Private Methods
624
            //-------------------------------------------------------------------------
625
            
626
            /**
627
             * Handles the completion of a test object's tests. Tallies test results 
628
             * from one level up to the next.
629
             * @param {TestNode} node The TestNode representing the test object.
630
             * @return {Void}
631
             * @method _handleTestObjectComplete
632
             * @private
633
             */
634
            _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
635
                if (Y.Lang.isObject(node.testObject)){
636
                    node.parent.results.passed += node.results.passed;
637
                    node.parent.results.failed += node.results.failed;
638
                    node.parent.results.total += node.results.total;                
639
                    node.parent.results.ignored += node.results.ignored;                
640
                    node.parent.results[node.testObject.name] = node.results;
641
                
642
                    if (node.testObject instanceof Y.Test.Suite){
643
                        node.testObject.tearDown();
644
                        this.fire(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
645
                    } else if (node.testObject instanceof Y.Test.Case){
646
                        this.fire(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
647
                    }      
648
                } 
649
            },                
650
            
651
            //-------------------------------------------------------------------------
652
            // Navigation Methods
653
            //-------------------------------------------------------------------------
654
            
655
            /**
656
             * Retrieves the next node in the test tree.
657
             * @return {TestNode} The next node in the test tree or null if the end is reached.
658
             * @private
659
             * @static
660
             * @method _next
661
             */
662
            _next : function () /*:TestNode*/ {
663
            
664
                if (this._cur.firstChild) {
665
                    this._cur = this._cur.firstChild;
666
                } else if (this._cur.next) {
667
                    this._cur = this._cur.next;            
668
                } else {
669
                    while (this._cur && !this._cur.next && this._cur !== this._root){
670
                        this._handleTestObjectComplete(this._cur);
671
                        this._cur = this._cur.parent;
672
                    }
673
                    
674
                    if (this._cur == this._root){
675
                        this._cur.results.type = "report";
676
                        this._cur.results.timestamp = (new Date()).toLocaleString();
677
                        this._cur.results.duration = (new Date()) - this._cur.results.duration;                            
678
                        this.fire(this.COMPLETE_EVENT, { results: this._cur.results});
679
                        this._cur = null;
680
                    } else {
681
                        this._handleTestObjectComplete(this._cur);               
682
                        this._cur = this._cur.next;                
683
                    }
684
                }
685
            
686
                return this._cur;
687
            },
688
            
689
            /**
690
             * Runs a test case or test suite, returning the results.
691
             * @param {Y.Test.Case|Y.Test.Suite} testObject The test case or test suite to run.
692
             * @return {Object} Results of the execution with properties passed, failed, and total.
693
             * @private
694
             * @method _run
695
             * @static
696
             */
697
            _run : function () /*:Void*/ {
698
            
699
                //flag to indicate if the TestRunner should wait before continuing
700
                var shouldWait /*:Boolean*/ = false;
701
                
702
                //get the next test node
703
                var node = this._next();
704
                
705
                if (node !== null) {
706
                    var testObject = node.testObject;
707
                    
708
                    //figure out what to do
709
                    if (Y.Lang.isObject(testObject)){
710
                        if (testObject instanceof Y.Test.Suite){
711
                            this.fire(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
712
                            testObject.setUp();
713
                        } else if (testObject instanceof Y.Test.Case){
714
                            this.fire(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
715
                        }
716
                        
717
                        //some environments don't support setTimeout
718
                        if (typeof setTimeout != "undefined"){                    
719
                            setTimeout(function(){
720
                                Y.Test.Runner._run();
721
                            }, 0);
722
                        } else {
723
                            this._run();
724
                        }
725
                    } else {
726
                        this._runTest(node);
727
                    }
728
    
729
                }
730
            },
731
            
732
            _resumeTest : function (segment /*:Function*/) /*:Void*/ {
733
            
734
                //get relevant information
735
                var node /*:TestNode*/ = this._cur;
736
                
737
                //if there's no node, it probably means a wait() was called after resume()
738
                if (!node){
739
                    //TODO: Handle in some way?
740
                    //console.log("wait() called after resume()");
741
                    //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
742
                    return;
743
                }
744
                
745
                var testName /*:String*/ = node.testObject;
746
                var testCase /*:Y.Test.Case*/ = node.parent.testObject;
747
            
748
                //cancel other waits if available
749
                if (testCase.__yui_wait){
750
                    clearTimeout(testCase.__yui_wait);
751
                    delete testCase.__yui_wait;
752
                }
753
754
                //get the "should" test cases
755
                var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
756
                var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
757
                
758
                //variable to hold whether or not the test failed
759
                var failed /*:Boolean*/ = false;
760
                var error /*:Error*/ = null;
761
                    
762
                //try the test
763
                try {
764
                
765
                    //run the test
766
                    segment.apply(testCase);
767
                    
768
                    //if it should fail, and it got here, then it's a fail because it didn't
769
                    if (shouldFail){
770
                        error = new Y.Assert.ShouldFail();
771
                        failed = true;
772
                    } else if (shouldError){
773
                        error = new Y.Assert.ShouldError();
774
                        failed = true;
775
                    }
776
                               
777
                } catch (thrown /*:Error*/){
778
779
                    //cancel any pending waits, the test already failed
780
                    if (testCase.__yui_wait){
781
                        clearTimeout(testCase.__yui_wait);
782
                        delete testCase.__yui_wait;
783
                    }                    
784
                
785
                    //figure out what type of error it was
786
                    if (thrown instanceof Y.Assert.Error) {
787
                        if (!shouldFail){
788
                            error = thrown;
789
                            failed = true;
790
                        }
791
                    } else if (thrown instanceof Y.Test.Wait){
792
                    
793
                        if (Y.Lang.isFunction(thrown.segment)){
794
                            if (Y.Lang.isNumber(thrown.delay)){
795
                            
796
                                //some environments don't support setTimeout
797
                                if (typeof setTimeout != "undefined"){
798
                                    testCase.__yui_wait = setTimeout(function(){
799
                                        Y.Test.Runner._resumeTest(thrown.segment);
800
                                    }, thrown.delay);
801
                                } else {
802
                                    throw new Error("Asynchronous tests not supported in this environment.");
803
                                }
804
                            }
805
                        }
806
                        
807
                        return;
808
                    
809
                    } else {
810
                        //first check to see if it should error
811
                        if (!shouldError) {                        
812
                            error = new Y.Assert.UnexpectedError(thrown);
813
                            failed = true;
814
                        } else {
815
                            //check to see what type of data we have
816
                            if (Y.Lang.isString(shouldError)){
817
                                
818
                                //if it's a string, check the error message
819
                                if (thrown.message != shouldError){
820
                                    error = new Y.Assert.UnexpectedError(thrown);
821
                                    failed = true;                                    
822
                                }
823
                            } else if (Y.Lang.isFunction(shouldError)){
824
                            
825
                                //if it's a function, see if the error is an instance of it
826
                                if (!(thrown instanceof shouldError)){
827
                                    error = new Y.Assert.UnexpectedError(thrown);
828
                                    failed = true;
829
                                }
830
                            
831
                            } else if (Y.Lang.isObject(shouldError)){
832
                            
833
                                //if it's an object, check the instance and message
834
                                if (!(thrown instanceof shouldError.constructor) || 
835
                                        thrown.message != shouldError.message){
836
                                    error = new Y.Assert.UnexpectedError(thrown);
837
                                    failed = true;                                    
838
                                }
839
                            
840
                            }
841
                        
842
                        }
843
                    }
844
                    
845
                }
846
                
847
                //fire appropriate event
848
                if (failed) {
849
                    this.fire(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
850
                } else {
851
                    this.fire(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
852
                }
853
                
854
                //run the tear down
855
                testCase.tearDown();
856
                
857
                //update results
858
                node.parent.results[testName] = { 
859
                    result: failed ? "fail" : "pass",
860
                    message: error ? error.getMessage() : "Test passed",
861
                    type: "test",
862
                    name: testName
863
                };
864
                
865
                if (failed){
866
                    node.parent.results.failed++;
867
                } else {
868
                    node.parent.results.passed++;
869
                }
870
                node.parent.results.total++;
871
    
872
                //set timeout not supported in all environments
873
                if (typeof setTimeout != "undefined"){
874
                    setTimeout(function(){
875
                        Y.Test.Runner._run();
876
                    }, 0);
877
                } else {
878
                    this._run();
879
                }
880
            
881
            },
882
                    
883
            /**
884
             * Runs a single test based on the data provided in the node.
885
             * @param {TestNode} node The TestNode representing the test to run.
886
             * @return {Void}
887
             * @static
888
             * @private
889
             * @name _runTest
890
             */
891
            _runTest : function (node /*:TestNode*/) /*:Void*/ {
892
            
893
                //get relevant information
894
                var testName /*:String*/ = node.testObject;
895
                var testCase /*:Y.Test.Case*/ = node.parent.testObject;
896
                var test /*:Function*/ = testCase[testName];
897
                
898
                //get the "should" test cases
899
                var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
900
                
901
                //figure out if the test should be ignored or not
902
                if (shouldIgnore){
903
                
904
                    //update results
905
                    node.parent.results[testName] = { 
906
                        result: "ignore",
907
                        message: "Test ignored",
908
                        type: "test",
909
                        name: testName
910
                    };
911
                    
912
                    node.parent.results.ignored++;
913
                    node.parent.results.total++;
914
                
915
                    this.fire(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
916
                    
917
                    //some environments don't support setTimeout
918
                    if (typeof setTimeout != "undefined"){                    
919
                        setTimeout(function(){
920
                            Y.Test.Runner._run();
921
                        }, 0);              
922
                    } else {
923
                        this._run();
924
                    }
925
    
926
                } else {
927
                
928
                    //run the setup
929
                    testCase.setUp();
930
                    
931
                    //now call the body of the test
932
                    this._resumeTest(test);                
933
                }
934
    
935
            },        
936
            
937
            //-------------------------------------------------------------------------
938
            // Protected Methods
939
            //-------------------------------------------------------------------------   
940
        
941
            /*
942
             * Fires events for the TestRunner. This overrides the default fire()
943
             * method from EventProvider to add the type property to the data that is
944
             * passed through on each event call.
945
             * @param {String} type The type of event to fire.
946
             * @param {Object} data (Optional) Data for the event.
947
             * @method fire
948
             * @static
949
             * @protected
950
             */
951
            fire : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
952
                data = data || {};
953
                data.type = type;
954
                TestRunner.superclass.fire.call(this, type, data);
955
            },
956
            
957
            //-------------------------------------------------------------------------
958
            // Public Methods
959
            //-------------------------------------------------------------------------   
960
        
961
            /**
962
             * Adds a test suite or test case to the list of test objects to run.
963
             * @param testObject Either a TestCase or a TestSuite that should be run.
964
             * @return {Void}
965
             * @method add
966
             * @static
967
             */
968
            add : function (testObject /*:Object*/) /*:Void*/ {
969
                this.masterSuite.add(testObject);
970
            },
971
            
972
            /**
973
             * Removes all test objects from the runner.
974
             * @return {Void}
975
             * @method clear
976
             * @static
977
             */
978
            clear : function () /*:Void*/ {
979
                this.masterSuite.items = [];
980
            },
981
            
982
            /**
983
             * Resumes the TestRunner after wait() was called.
984
             * @param {Function} segment The function to run as the rest
985
             *      of the haulted test.
986
             * @return {Void}
987
             * @method resume
988
             * @static
989
             */
990
            resume : function (segment /*:Function*/) /*:Void*/ {
991
                this._resumeTest(segment || function(){});
992
            },
993
        
994
            /**
995
             * Runs the test suite.
996
             * @return {Void}
997
             * @method run
998
             * @static
999
             */
1000
            run : function (testObject /*:Object*/) /*:Void*/ {
1001
                
1002
                //pointer to runner to avoid scope issues 
1003
                var runner = Y.Test.Runner;
1004
    
1005
                //build the test tree
1006
                runner._buildTestTree();
1007
                            
1008
                //set when the test started
1009
                runner._root.results.duration = (new Date()).valueOf();
1010
                
1011
                //fire the begin event
1012
                runner.fire(runner.BEGIN_EVENT);
1013
           
1014
                //begin the testing
1015
                runner._run();
1016
            }    
1017
        });
1018
        
1019
        return new TestRunner();
1020
        
1021
    })();
1022
1023
  
1024
    /**
1025
     * The Assert object provides functions to test JavaScript values against
1026
     * known and expected results. Whenever a comparison (assertion) fails,
1027
     * an error is thrown.
1028
     *
1029
     * @class Assert
1030
     * @static
1031
     */
1032
    Y.Assert = {
1033
    
1034
        //-------------------------------------------------------------------------
1035
        // Helper Methods
1036
        //-------------------------------------------------------------------------
1037
        
1038
        /**
1039
         * Formats a message so that it can contain the original assertion message
1040
         * in addition to the custom message.
1041
         * @param {String} customMessage The message passed in by the developer.
1042
         * @param {String} defaultMessage The message created by the error by default.
1043
         * @return {String} The final error message, containing either or both.
1044
         * @protected
1045
         * @static
1046
         * @method _formatMessage
1047
         */
1048
        _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
1049
            var message = customMessage;
1050
            if (Y.Lang.isString(customMessage) && customMessage.length > 0){
1051
                return Y.Lang.substitute(customMessage, { message: defaultMessage });
1052
            } else {
1053
                return defaultMessage;
1054
            }        
1055
        },
1056
        
1057
        //-------------------------------------------------------------------------
1058
        // Generic Assertion Methods
1059
        //-------------------------------------------------------------------------
1060
        
1061
        /** 
1062
         * Forces an assertion error to occur.
1063
         * @param {String} message (Optional) The message to display with the failure.
1064
         * @method fail
1065
         * @static
1066
         */
1067
        fail : function (message /*:String*/) /*:Void*/ {
1068
            throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Test force-failed."));
1069
        },       
1070
        
1071
        //-------------------------------------------------------------------------
1072
        // Equality Assertion Methods
1073
        //-------------------------------------------------------------------------    
1074
        
1075
        /**
1076
         * Asserts that a value is equal to another. This uses the double equals sign
1077
         * so type cohersion may occur.
1078
         * @param {Object} expected The expected value.
1079
         * @param {Object} actual The actual value to test.
1080
         * @param {String} message (Optional) The message to display if the assertion fails.
1081
         * @method areEqual
1082
         * @static
1083
         */
1084
        areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1085
            if (expected != actual) {
1086
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal."), expected, actual);
1087
            }
1088
        },
1089
        
1090
        /**
1091
         * Asserts that a value is not equal to another. This uses the double equals sign
1092
         * so type cohersion may occur.
1093
         * @param {Object} unexpected The unexpected value.
1094
         * @param {Object} actual The actual value to test.
1095
         * @param {String} message (Optional) The message to display if the assertion fails.
1096
         * @method areNotEqual
1097
         * @static
1098
         */
1099
        areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/, 
1100
                             message /*:String*/) /*:Void*/ {
1101
            if (unexpected == actual) {
1102
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be equal."), unexpected);
1103
            }
1104
        },
1105
        
1106
        /**
1107
         * Asserts that a value is not the same as another. This uses the triple equals sign
1108
         * so no type cohersion may occur.
1109
         * @param {Object} unexpected The unexpected value.
1110
         * @param {Object} actual The actual value to test.
1111
         * @param {String} message (Optional) The message to display if the assertion fails.
1112
         * @method areNotSame
1113
         * @static
1114
         */
1115
        areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1116
            if (unexpected === actual) {
1117
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be the same."), unexpected);
1118
            }
1119
        },
1120
    
1121
        /**
1122
         * Asserts that a value is the same as another. This uses the triple equals sign
1123
         * so no type cohersion may occur.
1124
         * @param {Object} expected The expected value.
1125
         * @param {Object} actual The actual value to test.
1126
         * @param {String} message (Optional) The message to display if the assertion fails.
1127
         * @method areSame
1128
         * @static
1129
         */
1130
        areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1131
            if (expected !== actual) {
1132
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be the same."), expected, actual);
1133
            }
1134
        },    
1135
        
1136
        //-------------------------------------------------------------------------
1137
        // Boolean Assertion Methods
1138
        //-------------------------------------------------------------------------    
1139
        
1140
        /**
1141
         * Asserts that a value is false. This uses the triple equals sign
1142
         * so no type cohersion may occur.
1143
         * @param {Object} actual The actual value to test.
1144
         * @param {String} message (Optional) The message to display if the assertion fails.
1145
         * @method isFalse
1146
         * @static
1147
         */
1148
        isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1149
            if (false !== actual) {
1150
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be false."), false, actual);
1151
            }
1152
        },
1153
        
1154
        /**
1155
         * Asserts that a value is true. This uses the triple equals sign
1156
         * so no type cohersion may occur.
1157
         * @param {Object} actual The actual value to test.
1158
         * @param {String} message (Optional) The message to display if the assertion fails.
1159
         * @method isTrue
1160
         * @static
1161
         */
1162
        isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1163
            if (true !== actual) {
1164
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be true."), true, actual);
1165
            }
1166
    
1167
        },
1168
        
1169
        //-------------------------------------------------------------------------
1170
        // Special Value Assertion Methods
1171
        //-------------------------------------------------------------------------    
1172
        
1173
        /**
1174
         * Asserts that a value is not a number.
1175
         * @param {Object} actual The value to test.
1176
         * @param {String} message (Optional) The message to display if the assertion fails.
1177
         * @method isNaN
1178
         * @static
1179
         */
1180
        isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1181
            if (!isNaN(actual)){
1182
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
1183
            }    
1184
        },
1185
        
1186
        /**
1187
         * Asserts that a value is not the special NaN value.
1188
         * @param {Object} actual The value to test.
1189
         * @param {String} message (Optional) The message to display if the assertion fails.
1190
         * @method isNotNaN
1191
         * @static
1192
         */
1193
        isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1194
            if (isNaN(actual)){
1195
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be NaN."), NaN);
1196
            }    
1197
        },
1198
        
1199
        /**
1200
         * Asserts that a value is not null. This uses the triple equals sign
1201
         * so no type cohersion may occur.
1202
         * @param {Object} actual The actual value to test.
1203
         * @param {String} message (Optional) The message to display if the assertion fails.
1204
         * @method isNotNull
1205
         * @static
1206
         */
1207
        isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1208
            if (Y.Lang.isNull(actual)) {
1209
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be null."), null);
1210
            }
1211
        },
1212
    
1213
        /**
1214
         * Asserts that a value is not undefined. This uses the triple equals sign
1215
         * so no type cohersion may occur.
1216
         * @param {Object} actual The actual value to test.
1217
         * @param {String} message (Optional) The message to display if the assertion fails.
1218
         * @method isNotUndefined
1219
         * @static
1220
         */
1221
        isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1222
            if (Y.Lang.isUndefined(actual)) {
1223
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should not be undefined."), undefined);
1224
            }
1225
        },
1226
    
1227
        /**
1228
         * Asserts that a value is null. This uses the triple equals sign
1229
         * so no type cohersion may occur.
1230
         * @param {Object} actual The actual value to test.
1231
         * @param {String} message (Optional) The message to display if the assertion fails.
1232
         * @method isNull
1233
         * @static
1234
         */
1235
        isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1236
            if (!Y.Lang.isNull(actual)) {
1237
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be null."), null, actual);
1238
            }
1239
        },
1240
            
1241
        /**
1242
         * Asserts that a value is undefined. This uses the triple equals sign
1243
         * so no type cohersion may occur.
1244
         * @param {Object} actual The actual value to test.
1245
         * @param {String} message (Optional) The message to display if the assertion fails.
1246
         * @method isUndefined
1247
         * @static
1248
         */
1249
        isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1250
            if (!Y.Lang.isUndefined(actual)) {
1251
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
1252
            }
1253
        },    
1254
        
1255
        //--------------------------------------------------------------------------
1256
        // Instance Assertion Methods
1257
        //--------------------------------------------------------------------------    
1258
       
1259
        /**
1260
         * Asserts that a value is an array.
1261
         * @param {Object} actual The value to test.
1262
         * @param {String} message (Optional) The message to display if the assertion fails.
1263
         * @method isArray
1264
         * @static
1265
         */
1266
        isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1267
            if (!Y.Lang.isArray(actual)){
1268
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an array."), actual);
1269
            }    
1270
        },
1271
       
1272
        /**
1273
         * Asserts that a value is a Boolean.
1274
         * @param {Object} actual The value to test.
1275
         * @param {String} message (Optional) The message to display if the assertion fails.
1276
         * @method isBoolean
1277
         * @static
1278
         */
1279
        isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1280
            if (!Y.Lang.isBoolean(actual)){
1281
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a Boolean."), actual);
1282
            }    
1283
        },
1284
       
1285
        /**
1286
         * Asserts that a value is a function.
1287
         * @param {Object} actual The value to test.
1288
         * @param {String} message (Optional) The message to display if the assertion fails.
1289
         * @method isFunction
1290
         * @static
1291
         */
1292
        isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1293
            if (!Y.Lang.isFunction(actual)){
1294
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a function."), actual);
1295
            }    
1296
        },
1297
       
1298
        /**
1299
         * Asserts that a value is an instance of a particular object. This may return
1300
         * incorrect results when comparing objects from one frame to constructors in
1301
         * another frame. For best results, don't use in a cross-frame manner.
1302
         * @param {Function} expected The function that the object should be an instance of.
1303
         * @param {Object} actual The object to test.
1304
         * @param {String} message (Optional) The message to display if the assertion fails.
1305
         * @method isInstanceOf
1306
         * @static
1307
         */
1308
        isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1309
            if (!(actual instanceof expected)){
1310
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1311
            }
1312
        },
1313
        
1314
        /**
1315
         * Asserts that a value is a number.
1316
         * @param {Object} actual The value to test.
1317
         * @param {String} message (Optional) The message to display if the assertion fails.
1318
         * @method isNumber
1319
         * @static
1320
         */
1321
        isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1322
            if (!Y.Lang.isNumber(actual)){
1323
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a number."), actual);
1324
            }    
1325
        },    
1326
        
1327
        /**
1328
         * Asserts that a value is an object.
1329
         * @param {Object} actual The value to test.
1330
         * @param {String} message (Optional) The message to display if the assertion fails.
1331
         * @method isObject
1332
         * @static
1333
         */
1334
        isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1335
            if (!Y.Lang.isObject(actual)){
1336
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an object."), actual);
1337
            }
1338
        },
1339
        
1340
        /**
1341
         * Asserts that a value is a string.
1342
         * @param {Object} actual The value to test.
1343
         * @param {String} message (Optional) The message to display if the assertion fails.
1344
         * @method isString
1345
         * @static
1346
         */
1347
        isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1348
            if (!Y.Lang.isString(actual)){
1349
                throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a string."), actual);
1350
            }
1351
        },
1352
        
1353
        /**
1354
         * Asserts that a value is of a particular type. 
1355
         * @param {String} expectedType The expected type of the variable.
1356
         * @param {Object} actualValue The actual value to test.
1357
         * @param {String} message (Optional) The message to display if the assertion fails.
1358
         * @method isTypeOf
1359
         * @static
1360
         */
1361
        isTypeOf : function (expectedType /*:String*/, actualValue /*:Object*/, message /*:String*/) /*:Void*/{
1362
            if (typeof actualValue != expectedType){
1363
                throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expected, typeof actualValue);
1364
            }
1365
        }
1366
    };
1367
    
1368
    //-----------------------------------------------------------------------------
1369
    // Assertion errors
1370
    //-----------------------------------------------------------------------------
1371
    
1372
    /**
1373
     * Error is thrown whenever an assertion fails. It provides methods
1374
     * to more easily get at error information and also provides a base class
1375
     * from which more specific assertion errors can be derived.
1376
     *
1377
     * @param {String} message The message to display when the error occurs.
1378
     * @namespace Assert
1379
     * @class Error
1380
     * @constructor
1381
     */ 
1382
    Y.Assert.Error = function (message /*:String*/){
1383
    
1384
        //call superclass
1385
        arguments.callee.superclass.constructor.call(this, message);
1386
        
1387
        /*
1388
         * Error message. Must be duplicated to ensure browser receives it.
1389
         * @type String
1390
         * @property message
1391
         */
1392
        this.message /*:String*/ = message;
1393
        
1394
        /**
1395
         * The name of the error that occurred.
1396
         * @type String
1397
         * @property name
1398
         */
1399
        this.name /*:String*/ = "Assert Error";
1400
    };
1401
    
1402
    //inherit methods
1403
    Y.extend(Y.Assert.Error, Error, {
1404
    
1405
        /**
1406
         * Returns a fully formatted error for an assertion failure. This should
1407
         * be overridden by all subclasses to provide specific information.
1408
         * @method getMessage
1409
         * @return {String} A string describing the error.
1410
         */
1411
        getMessage : function () /*:String*/ {
1412
            return this.message;
1413
        },
1414
        
1415
        /**
1416
         * Returns a string representation of the error.
1417
         * @method toString
1418
         * @return {String} A string representation of the error.
1419
         */
1420
        toString : function () /*:String*/ {
1421
            return this.name + ": " + this.getMessage();
1422
        },
1423
        
1424
        /**
1425
         * Returns a primitive value version of the error. Same as toString().
1426
         * @method valueOf
1427
         * @return {String} A primitive value version of the error.
1428
         */
1429
        valueOf : function () /*:String*/ {
1430
            return this.toString();
1431
        }
1432
    
1433
    });
1434
    
1435
    /**
1436
     * ComparisonFailure is subclass of Error that is thrown whenever
1437
     * a comparison between two values fails. It provides mechanisms to retrieve
1438
     * both the expected and actual value.
1439
     *
1440
     * @param {String} message The message to display when the error occurs.
1441
     * @param {Object} expected The expected value.
1442
     * @param {Object} actual The actual value that caused the assertion to fail.
1443
     * @namespace Assert 
1444
     * @extends Assert.Error
1445
     * @class ComparisonFailure
1446
     * @constructor
1447
     */ 
1448
    Y.Assert.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1449
    
1450
        //call superclass
1451
        arguments.callee.superclass.constructor.call(this, message);
1452
        
1453
        /**
1454
         * The expected value.
1455
         * @type Object
1456
         * @property expected
1457
         */
1458
        this.expected /*:Object*/ = expected;
1459
        
1460
        /**
1461
         * The actual value.
1462
         * @type Object
1463
         * @property actual
1464
         */
1465
        this.actual /*:Object*/ = actual;
1466
        
1467
        /**
1468
         * The name of the error that occurred.
1469
         * @type String
1470
         * @property name
1471
         */
1472
        this.name /*:String*/ = "ComparisonFailure";
1473
        
1474
    };
1475
    
1476
    //inherit methods
1477
    Y.extend(Y.Assert.ComparisonFailure, Y.Assert.Error, {
1478
    
1479
        /**
1480
         * Returns a fully formatted error for an assertion failure. This message
1481
         * provides information about the expected and actual values.
1482
         * @method toString
1483
         * @return {String} A string describing the error.
1484
         */
1485
        getMessage : function () /*:String*/ {
1486
            return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
1487
                "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1488
        }
1489
    
1490
    });
1491
    
1492
    /**
1493
     * UnexpectedValue is subclass of Error that is thrown whenever
1494
     * a value was unexpected in its scope. This typically means that a test
1495
     * was performed to determine that a value was *not* equal to a certain
1496
     * value.
1497
     *
1498
     * @param {String} message The message to display when the error occurs.
1499
     * @param {Object} unexpected The unexpected value.
1500
     * @namespace Assert
1501
     * @extends Assert.Error
1502
     * @class UnexpectedValue
1503
     * @constructor
1504
     */ 
1505
    Y.Assert.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1506
    
1507
        //call superclass
1508
        arguments.callee.superclass.constructor.call(this, message);
1509
        
1510
        /**
1511
         * The unexpected value.
1512
         * @type Object
1513
         * @property unexpected
1514
         */
1515
        this.unexpected /*:Object*/ = unexpected;
1516
        
1517
        /**
1518
         * The name of the error that occurred.
1519
         * @type String
1520
         * @property name
1521
         */
1522
        this.name /*:String*/ = "UnexpectedValue";
1523
        
1524
    };
1525
    
1526
    //inherit methods
1527
    Y.extend(Y.Assert.UnexpectedValue, Y.Assert.Error, {
1528
    
1529
        /**
1530
         * Returns a fully formatted error for an assertion failure. The message
1531
         * contains information about the unexpected value that was encountered.
1532
         * @method getMessage
1533
         * @return {String} A string describing the error.
1534
         */
1535
        getMessage : function () /*:String*/ {
1536
            return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1537
        }
1538
    
1539
    });
1540
    
1541
    /**
1542
     * ShouldFail is subclass of Error that is thrown whenever
1543
     * a test was expected to fail but did not.
1544
     *
1545
     * @param {String} message The message to display when the error occurs.
1546
     * @namespace Assert
1547
     * @extends Assert.Error
1548
     * @class ShouldFail
1549
     * @constructor
1550
     */  
1551
    Y.Assert.ShouldFail = function (message /*:String*/){
1552
    
1553
        //call superclass
1554
        arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
1555
        
1556
        /**
1557
         * The name of the error that occurred.
1558
         * @type String
1559
         * @property name
1560
         */
1561
        this.name /*:String*/ = "ShouldFail";
1562
        
1563
    };
1564
    
1565
    //inherit methods
1566
    Y.extend(Y.Assert.ShouldFail, Y.Assert.Error);
1567
    
1568
    /**
1569
     * ShouldError is subclass of Error that is thrown whenever
1570
     * a test is expected to throw an error but doesn't.
1571
     *
1572
     * @param {String} message The message to display when the error occurs.
1573
     * @namespace Assert
1574
     * @extends Assert.Error
1575
     * @class ShouldError
1576
     * @constructor
1577
     */  
1578
    Y.Assert.ShouldError = function (message /*:String*/){
1579
    
1580
        //call superclass
1581
        arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
1582
        
1583
        /**
1584
         * The name of the error that occurred.
1585
         * @type String
1586
         * @property name
1587
         */
1588
        this.name /*:String*/ = "ShouldError";
1589
        
1590
    };
1591
    
1592
    //inherit methods
1593
    Y.extend(Y.Assert.ShouldError, Y.Assert.Error);
1594
    
1595
    /**
1596
     * UnexpectedError is subclass of Error that is thrown whenever
1597
     * an error occurs within the course of a test and the test was not expected
1598
     * to throw an error.
1599
     *
1600
     * @param {Error} cause The unexpected error that caused this error to be 
1601
     *                      thrown.
1602
     * @namespace Assert
1603
     * @extends Assert.Error
1604
     * @class UnexpectedError
1605
     * @constructor
1606
     */  
1607
    Y.Assert.UnexpectedError = function (cause /*:Object*/){
1608
    
1609
        //call superclass
1610
        arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
1611
        
1612
        /**
1613
         * The unexpected error that occurred.
1614
         * @type Error
1615
         * @property cause
1616
         */
1617
        this.cause /*:Error*/ = cause;
1618
        
1619
        /**
1620
         * The name of the error that occurred.
1621
         * @type String
1622
         * @property name
1623
         */
1624
        this.name /*:String*/ = "UnexpectedError";
1625
        
1626
        /**
1627
         * Stack information for the error (if provided).
1628
         * @type String
1629
         * @property stack
1630
         */
1631
        this.stack /*:String*/ = cause.stack;
1632
        
1633
    };
1634
    
1635
    //inherit methods
1636
    Y.extend(Y.Assert.UnexpectedError, Y.Assert.Error);
1637
    
1638
1639
    
1640
    /**
1641
     * The ArrayAssert object provides functions to test JavaScript array objects
1642
     * for a variety of cases.
1643
     *
1644
     * @class ArrayAssert
1645
     * @static
1646
     */
1647
     
1648
    Y.ArrayAssert = {
1649
    
1650
        /**
1651
         * Asserts that a value is present in an array. This uses the triple equals 
1652
         * sign so no type cohersion may occur.
1653
         * @param {Object} needle The value that is expected in the array.
1654
         * @param {Array} haystack An array of values.
1655
         * @param {String} message (Optional) The message to display if the assertion fails.
1656
         * @method contains
1657
         * @static
1658
         */
1659
        contains : function (needle /*:Object*/, haystack /*:Array*/, 
1660
                               message /*:String*/) /*:Void*/ {
1661
            
1662
            var found /*:Boolean*/ = false;
1663
            
1664
            //begin checking values
1665
            for (var i=0; i < haystack.length && !found; i++){
1666
                if (haystack[i] === needle) {
1667
                    found = true;
1668
                }
1669
            }
1670
            
1671
            if (!found){
1672
                Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1673
            }
1674
        },
1675
    
1676
        /**
1677
         * Asserts that a set of values are present in an array. This uses the triple equals 
1678
         * sign so no type cohersion may occur. For this assertion to pass, all values must
1679
         * be found.
1680
         * @param {Object[]} needles An array of values that are expected in the array.
1681
         * @param {Array} haystack An array of values to check.
1682
         * @param {String} message (Optional) The message to display if the assertion fails.
1683
         * @method containsItems
1684
         * @static
1685
         */
1686
        containsItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1687
                               message /*:String*/) /*:Void*/ {
1688
    
1689
            //begin checking values
1690
            for (var i=0; i < needles.length; i++){
1691
                this.contains(needles[i], haystack, message);
1692
            }
1693
        },
1694
    
1695
        /**
1696
         * Asserts that a value matching some condition is present in an array. This uses
1697
         * a function to determine a match.
1698
         * @param {Function} matcher A function that returns true if the items matches or false if not.
1699
         * @param {Array} haystack An array of values.
1700
         * @param {String} message (Optional) The message to display if the assertion fails.
1701
         * @method containsMatch
1702
         * @static
1703
         */
1704
        containsMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1705
                               message /*:String*/) /*:Void*/ {
1706
            
1707
            //check for valid matcher
1708
            if (typeof matcher != "function"){
1709
                throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1710
            }
1711
            
1712
            var found /*:Boolean*/ = false;
1713
            
1714
            //begin checking values
1715
            for (var i=0; i < haystack.length && !found; i++){
1716
                if (matcher(haystack[i])) {
1717
                    found = true;
1718
                }
1719
            }
1720
            
1721
            if (!found){
1722
                Y.Assert.fail(Y.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1723
            }
1724
        },
1725
    
1726
        /**
1727
         * Asserts that a value is not present in an array. This uses the triple equals 
1728
         * Asserts that a value is not present in an array. This uses the triple equals 
1729
         * sign so no type cohersion may occur.
1730
         * @param {Object} needle The value that is expected in the array.
1731
         * @param {Array} haystack An array of values.
1732
         * @param {String} message (Optional) The message to display if the assertion fails.
1733
         * @method doesNotContain
1734
         * @static
1735
         */
1736
        doesNotContain : function (needle /*:Object*/, haystack /*:Array*/, 
1737
                               message /*:String*/) /*:Void*/ {
1738
            
1739
            var found /*:Boolean*/ = false;
1740
            
1741
            //begin checking values
1742
            for (var i=0; i < haystack.length && !found; i++){
1743
                if (haystack[i] === needle) {
1744
                    found = true;
1745
                }
1746
            }
1747
            
1748
            if (found){
1749
                Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1750
            }
1751
        },
1752
    
1753
        /**
1754
         * Asserts that a set of values are not present in an array. This uses the triple equals 
1755
         * sign so no type cohersion may occur. For this assertion to pass, all values must
1756
         * not be found.
1757
         * @param {Object[]} needles An array of values that are not expected in the array.
1758
         * @param {Array} haystack An array of values to check.
1759
         * @param {String} message (Optional) The message to display if the assertion fails.
1760
         * @method doesNotContainItems
1761
         * @static
1762
         */
1763
        doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1764
                               message /*:String*/) /*:Void*/ {
1765
    
1766
            for (var i=0; i < needles.length; i++){
1767
                this.doesNotContain(needles[i], haystack, message);
1768
            }
1769
    
1770
        },
1771
            
1772
        /**
1773
         * Asserts that no values matching a condition are present in an array. This uses
1774
         * a function to determine a match.
1775
         * @param {Function} matcher A function that returns true if the items matches or false if not.
1776
         * @param {Array} haystack An array of values.
1777
         * @param {String} message (Optional) The message to display if the assertion fails.
1778
         * @method doesNotContainMatch
1779
         * @static
1780
         */
1781
        doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1782
                               message /*:String*/) /*:Void*/ {
1783
            
1784
            //check for valid matcher
1785
            if (typeof matcher != "function"){
1786
                throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1787
            }
1788
    
1789
            var found /*:Boolean*/ = false;
1790
            
1791
            //begin checking values
1792
            for (var i=0; i < haystack.length && !found; i++){
1793
                if (matcher(haystack[i])) {
1794
                    found = true;
1795
                }
1796
            }
1797
            
1798
            if (found){
1799
                Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1800
            }
1801
        },
1802
            
1803
        /**
1804
         * Asserts that the given value is contained in an array at the specified index.
1805
         * This uses the triple equals sign so no type cohersion will occur.
1806
         * @param {Object} needle The value to look for.
1807
         * @param {Array} haystack The array to search in.
1808
         * @param {int} index The index at which the value should exist.
1809
         * @param {String} message (Optional) The message to display if the assertion fails.
1810
         * @method indexOf
1811
         * @static
1812
         */
1813
        indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1814
        
1815
            //try to find the value in the array
1816
            for (var i=0; i < haystack.length; i++){
1817
                if (haystack[i] === needle){
1818
                    Y.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1819
                    return;
1820
                }
1821
            }
1822
            
1823
            //if it makes it here, it wasn't found at all
1824
            Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1825
        },
1826
            
1827
        /**
1828
         * Asserts that the values in an array are equal, and in the same position,
1829
         * as values in another array. This uses the double equals sign
1830
         * so type cohersion may occur. Note that the array objects themselves
1831
         * need not be the same for this test to pass.
1832
         * @param {Array} expected An array of the expected values.
1833
         * @param {Array} actual Any array of the actual values.
1834
         * @param {String} message (Optional) The message to display if the assertion fails.
1835
         * @method itemsAreEqual
1836
         * @static
1837
         */
1838
        itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/, 
1839
                               message /*:String*/) /*:Void*/ {
1840
            
1841
            //one may be longer than the other, so get the maximum length
1842
            var len /*:int*/ = Math.max(expected.length, actual.length);
1843
           
1844
            //begin checking values
1845
            for (var i=0; i < len; i++){
1846
                Y.Assert.areEqual(expected[i], actual[i], 
1847
                    Y.Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1848
            }
1849
        },
1850
        
1851
        /**
1852
         * Asserts that the values in an array are equivalent, and in the same position,
1853
         * as values in another array. This uses a function to determine if the values
1854
         * are equivalent. Note that the array objects themselves
1855
         * need not be the same for this test to pass.
1856
         * @param {Array} expected An array of the expected values.
1857
         * @param {Array} actual Any array of the actual values.
1858
         * @param {Function} comparator A function that returns true if the values are equivalent
1859
         *      or false if not.
1860
         * @param {String} message (Optional) The message to display if the assertion fails.
1861
         * @return {Void}
1862
         * @method itemsAreEquivalent
1863
         * @static
1864
         */
1865
        itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/, 
1866
                               comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1867
            
1868
            //make sure the comparator is valid
1869
            if (typeof comparator != "function"){
1870
                throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1871
            }
1872
            
1873
            //one may be longer than the other, so get the maximum length
1874
            var len /*:int*/ = Math.max(expected.length, actual.length);
1875
            
1876
            //begin checking values
1877
            for (var i=0; i < len; i++){
1878
                if (!comparator(expected[i], actual[i])){
1879
                    throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1880
                }
1881
            }
1882
        },
1883
        
1884
        /**
1885
         * Asserts that an array is empty.
1886
         * @param {Array} actual The array to test.
1887
         * @param {String} message (Optional) The message to display if the assertion fails.
1888
         * @method isEmpty
1889
         * @static
1890
         */
1891
        isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1892
            if (actual.length > 0){
1893
                Y.Assert.fail(Y.Assert._formatMessage(message, "Array should be empty."));
1894
            }
1895
        },    
1896
        
1897
        /**
1898
         * Asserts that an array is not empty.
1899
         * @param {Array} actual The array to test.
1900
         * @param {String} message (Optional) The message to display if the assertion fails.
1901
         * @method isNotEmpty
1902
         * @static
1903
         */
1904
        isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1905
            if (actual.length === 0){
1906
                Y.Assert.fail(Y.Assert._formatMessage(message, "Array should not be empty."));
1907
            }
1908
        },    
1909
        
1910
        /**
1911
         * Asserts that the values in an array are the same, and in the same position,
1912
         * as values in another array. This uses the triple equals sign
1913
         * so no type cohersion will occur. Note that the array objects themselves
1914
         * need not be the same for this test to pass.
1915
         * @param {Array} expected An array of the expected values.
1916
         * @param {Array} actual Any array of the actual values.
1917
         * @param {String} message (Optional) The message to display if the assertion fails.
1918
         * @method itemsAreSame
1919
         * @static
1920
         */
1921
        itemsAreSame : function (expected /*:Array*/, actual /*:Array*/, 
1922
                              message /*:String*/) /*:Void*/ {
1923
            
1924
            //one may be longer than the other, so get the maximum length
1925
            var len /*:int*/ = Math.max(expected.length, actual.length);
1926
            
1927
            //begin checking values
1928
            for (var i=0; i < len; i++){
1929
                Y.Assert.areSame(expected[i], actual[i], 
1930
                    Y.Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1931
            }
1932
        },
1933
        
1934
        /**
1935
         * Asserts that the given value is contained in an array at the specified index,
1936
         * starting from the back of the array.
1937
         * This uses the triple equals sign so no type cohersion will occur.
1938
         * @param {Object} needle The value to look for.
1939
         * @param {Array} haystack The array to search in.
1940
         * @param {int} index The index at which the value should exist.
1941
         * @param {String} message (Optional) The message to display if the assertion fails.
1942
         * @method lastIndexOf
1943
         * @static
1944
         */
1945
        lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1946
        
1947
            //try to find the value in the array
1948
            for (var i=haystack.length; i >= 0; i--){
1949
                if (haystack[i] === needle){
1950
                    Y.Assert.areEqual(index, i, Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1951
                    return;
1952
                }
1953
            }
1954
            
1955
            //if it makes it here, it wasn't found at all
1956
            Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array."));        
1957
        }
1958
        
1959
    };
1960
1961
1962
    /**
1963
     * The ObjectAssert object provides functions to test JavaScript objects
1964
     * for a variety of cases.
1965
     *
1966
     * @class ObjectAssert
1967
     * @static
1968
     */
1969
    Y.ObjectAssert = {
1970
    
1971
        areEqual: function(expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1972
            Y.Object.each(expected, function(value, name){
1973
                Y.Assert.areEqual(expected[name], actual[name], Y.Assert._formatMessage(message, "Values should be equal for property " + name));
1974
            });            
1975
        },
1976
        
1977
        /**
1978
         * Asserts that an object has a property with the given name.
1979
         * @param {String} propertyName The name of the property to test.
1980
         * @param {Object} object The object to search.
1981
         * @param {String} message (Optional) The message to display if the assertion fails.
1982
         * @method has
1983
         * @static
1984
         */    
1985
        has : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1986
            if (!(propertyName in object)){
1987
                Y.Assert.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1988
            }    
1989
        },
1990
        
1991
        /**
1992
         * Asserts that an object has all properties of a reference object.
1993
         * @param {Object} refObject The object whose properties should be on the object to test.
1994
         * @param {Object} object The object to search.
1995
         * @param {String} message (Optional) The message to display if the assertion fails.
1996
         * @method hasAll
1997
         * @static
1998
         */    
1999
        hasAll : function (refObject /*:Object*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2000
            Y.Object.each(refObject, function(value, name){
2001
                if (!(name in object)){
2002
                    Y.Assert.fail(Y.Assert._formatMessage(message, "Property '" + name + "' not found on object."));
2003
                }    
2004
            });
2005
        },
2006
        
2007
        /**
2008
         * Asserts that a property with the given name exists on an object instance (not on its prototype).
2009
         * @param {String} propertyName The name of the property to test.
2010
         * @param {Object} object The object to search.
2011
         * @param {String} message (Optional) The message to display if the assertion fails.
2012
         * @method owns
2013
         * @static
2014
         */    
2015
        owns : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2016
            if (!Y.Object.owns(object, propertyName)){
2017
                Y.Assert.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
2018
            }     
2019
        },
2020
        
2021
        /**
2022
         * Asserts that all properties on a given object also exist on an object instance (not on its prototype).
2023
         * @param {Object} refObject The object whose properties should be owned by the object to test.
2024
         * @param {Object} object The object to search.
2025
         * @param {String} message (Optional) The message to display if the assertion fails.
2026
         * @method ownsAll
2027
         * @static
2028
         */    
2029
        ownsAll : function (refObject /*:Object*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2030
            Y.Object.each(refObject, function(value, name){
2031
                if (!Y.Object.owns(object, name)){
2032
                    Y.Assert.fail(Y.Assert._formatMessage(message, "Property '" + name + "' not found on object instance."));
2033
                }     
2034
            });
2035
        }
2036
    };
2037
2038
2039
    
2040
    /**
2041
     * The DateAssert object provides functions to test JavaScript Date objects
2042
     * for a variety of cases.
2043
     *
2044
     * @class DateAssert
2045
     * @static
2046
     */
2047
     
2048
    Y.DateAssert = {
2049
    
2050
        /**
2051
         * Asserts that a date's month, day, and year are equal to another date's.
2052
         * @param {Date} expected The expected date.
2053
         * @param {Date} actual The actual date to test.
2054
         * @param {String} message (Optional) The message to display if the assertion fails.
2055
         * @method datesAreEqual
2056
         * @static
2057
         */
2058
        datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2059
            if (expected instanceof Date && actual instanceof Date){
2060
                Y.AssertareEqual(expected.getFullYear(), actual.getFullYear(), Y.Assert_formatMessage(message, "Years should be equal."));
2061
                Y.AssertareEqual(expected.getMonth(), actual.getMonth(), Y.Assert_formatMessage(message, "Months should be equal."));
2062
                Y.AssertareEqual(expected.getDate(), actual.getDate(), Y.Assert_formatMessage(message, "Day of month should be equal."));
2063
            } else {
2064
                throw new TypeError("DateY.AssertdatesAreEqual(): Expected and actual values must be Date objects.");
2065
            }
2066
        },
2067
    
2068
        /**
2069
         * Asserts that a date's hour, minutes, and seconds are equal to another date's.
2070
         * @param {Date} expected The expected date.
2071
         * @param {Date} actual The actual date to test.
2072
         * @param {String} message (Optional) The message to display if the assertion fails.
2073
         * @method timesAreEqual
2074
         * @static
2075
         */
2076
        timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2077
            if (expected instanceof Date && actual instanceof Date){
2078
                Y.AssertareEqual(expected.getHours(), actual.getHours(), Y.Assert_formatMessage(message, "Hours should be equal."));
2079
                Y.AssertareEqual(expected.getMinutes(), actual.getMinutes(), Y.Assert_formatMessage(message, "Minutes should be equal."));
2080
                Y.AssertareEqual(expected.getSeconds(), actual.getSeconds(), Y.Assert_formatMessage(message, "Seconds should be equal."));
2081
            } else {
2082
                throw new TypeError("DateY.AsserttimesAreEqual(): Expected and actual values must be Date objects.");
2083
            }
2084
        }
2085
        
2086
    };
2087
2088
    
2089
    Y.namespace("Test.Format");
2090
    
2091
    /**
2092
     * Returns test results formatted as a JSON string. Requires JSON utility.
2093
     * @param {Object} result The results object created by TestRunner.
2094
     * @return {String} A JSON-formatted string of results.
2095
     * @namespace Test.Format
2096
     * @method JSON
2097
     * @static
2098
     */
2099
    Y.Test.Format.JSON = function(results /*:Object*/) /*:String*/ {
2100
        return Y.JSON.stringify(results);
2101
    };
2102
    
2103
    /**
2104
     * Returns test results formatted as an XML string.
2105
     * @param {Object} result The results object created by TestRunner.
2106
     * @return {String} An XML-formatted string of results.
2107
     * @namespace Test.Format
2108
     * @method XML
2109
     * @static
2110
     */
2111
    Y.Test.Format.XML = function(results /*:Object*/) /*:String*/ {
2112
    
2113
        var l = Y.Lang;
2114
        var xml /*:String*/ = "<" + results.type + " name=\"" + results.name.replace(/"/g, "&quot;").replace(/'/g, "&apos;") + "\"";
2115
        
2116
        if (results.type == "test"){
2117
            xml += " result=\"" + results.result + "\" message=\"" + results.message + "\">";
2118
        } else {
2119
            xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2120
            for (var prop in results) {
2121
                if (Y.Object.owns(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2122
                    xml += arguments.callee(results[prop]);
2123
                }
2124
            }        
2125
        }
2126
    
2127
        xml += "</" + results.type + ">";
2128
        
2129
        return xml;
2130
    
2131
    };
2132
2133
2134
    Y.namespace("Test");
2135
    
2136
    /**
2137
     * An object capable of sending test results to a server.
2138
     * @param {String} url The URL to submit the results to.
2139
     * @param {Function} format (Optiona) A function that outputs the results in a specific format.
2140
     *      Default is Y.Test.Format.XML.
2141
     * @constructor
2142
     * @namespace Test
2143
     * @class Reporter
2144
     */
2145
    Y.Test.Reporter = function(url /*:String*/, format /*:Function*/) {
2146
    
2147
        /**
2148
         * The URL to submit the data to.
2149
         * @type String
2150
         * @property url
2151
         */
2152
        this.url /*:String*/ = url;
2153
    
2154
        /**
2155
         * The formatting function to call when submitting the data.
2156
         * @type Function
2157
         * @property format
2158
         */
2159
        this.format /*:Function*/ = format || Y.Test.Format.XML;
2160
    
2161
        /**
2162
         * Extra fields to submit with the request.
2163
         * @type Object
2164
         * @property _fields
2165
         * @private
2166
         */
2167
        this._fields /*:Object*/ = new Object();
2168
        
2169
        /**
2170
         * The form element used to submit the results.
2171
         * @type HTMLFormElement
2172
         * @property _form
2173
         * @private
2174
         */
2175
        this._form /*:HTMLElement*/ = null;
2176
    
2177
        /**
2178
         * Iframe used as a target for form submission.
2179
         * @type HTMLIFrameElement
2180
         * @property _iframe
2181
         * @private
2182
         */
2183
        this._iframe /*:HTMLElement*/ = null;
2184
    };
2185
    
2186
    Y.Test.Reporter.prototype = {
2187
    
2188
        //restore missing constructor
2189
        constructor: Y.Test.Reporter,
2190
    
2191
        /**
2192
         * Adds a field to the form that submits the results.
2193
         * @param {String} name The name of the field.
2194
         * @param {Variant} value The value of the field.
2195
         * @return {Void}
2196
         * @method addField
2197
         */
2198
        addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
2199
            this._fields[name] = value;    
2200
        },
2201
        
2202
        /**
2203
         * Removes all previous defined fields.
2204
         * @return {Void}
2205
         * @method addField
2206
         */
2207
        clearFields : function() /*:Void*/{
2208
            this._fields = new Object();
2209
        },
2210
    
2211
        /**
2212
         * Cleans up the memory associated with the TestReporter, removing DOM elements
2213
         * that were created.
2214
         * @return {Void}
2215
         * @method destroy
2216
         */
2217
        destroy : function() /*:Void*/ {
2218
            if (this._form){
2219
                this._form.parentNode.removeChild(this._form);
2220
                this._form = null;
2221
            }        
2222
            if (this._iframe){
2223
                this._iframe.parentNode.removeChild(this._iframe);
2224
                this._iframe = null;
2225
            }
2226
            this._fields = null;
2227
        },
2228
    
2229
        /**
2230
         * Sends the report to the server.
2231
         * @param {Object} results The results object created by TestRunner.
2232
         * @return {Void}
2233
         * @method report
2234
         */
2235
        report : function(results /*:Object*/) /*:Void*/{
2236
        
2237
            //if the form hasn't been created yet, create it
2238
            if (!this._form){
2239
                this._form = document.createElement("form");
2240
                this._form.method = "post";
2241
                this._form.style.visibility = "hidden";
2242
                this._form.style.position = "absolute";
2243
                this._form.style.top = 0;
2244
                document.body.appendChild(this._form);
2245
            
2246
                //IE won't let you assign a name using the DOM, must do it the hacky way
2247
                if (Y.UA.ie){
2248
                    this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
2249
                } else {
2250
                    this._iframe = document.createElement("iframe");
2251
                    this._iframe.name = "yuiTestTarget";
2252
                }
2253
    
2254
                this._iframe.src = "javascript:false";
2255
                this._iframe.style.visibility = "hidden";
2256
                this._iframe.style.position = "absolute";
2257
                this._iframe.style.top = 0;
2258
                document.body.appendChild(this._iframe);
2259
    
2260
                this._form.target = "yuiTestTarget";
2261
            }
2262
    
2263
            //set the form's action
2264
            this._form.action = this.url;
2265
        
2266
            //remove any existing fields
2267
            while(this._form.hasChildNodes()){
2268
                this._form.removeChild(this._form.lastChild);
2269
            }
2270
            
2271
            //create default fields
2272
            this._fields.results = this.format(results);
2273
            this._fields.useragent = navigator.userAgent;
2274
            this._fields.timestamp = (new Date()).toLocaleString();
2275
    
2276
            //add fields to the form
2277
            for (var prop in this._fields){
2278
                if (Y.Object.owns(this._fields, prop) && typeof this._fields[prop] != "function"){
2279
                    var input = document.createElement("input");
2280
                    input.type = "hidden";
2281
                    input.name = prop;
2282
                    input.value = this._fields[prop];
2283
                    this._form.appendChild(input);
2284
                }
2285
            }
2286
    
2287
            //remove default fields
2288
            delete this._fields.results;
2289
            delete this._fields.useragent;
2290
            delete this._fields.timestamp;
2291
            
2292
            if (arguments[1] !== false){
2293
                this._form.submit();
2294
            }
2295
        
2296
        }
2297
    
2298
    };
2299
2300
    /**
2301
     * Creates a new mock object.
2302
     * @class Mock
2303
     * @constructor
2304
     * @param {Object} template (Optional) An object whose methods
2305
     *      should be stubbed out on the mock object.
2306
     */
2307
    Y.Mock = function(template){
2308
    
2309
        //use blank object is nothing is passed in
2310
        template = template || {};
2311
        
2312
        var mock = null;
2313
        
2314
        //try to create mock that keeps prototype chain intact
2315
        try {
2316
            mock = Y.Object(template);
2317
        } catch (ex) {
2318
            mock = {};
2319
            Y.log("Couldn't create mock with prototype.", "warn", "Mock");
2320
        }
2321
        
2322
        //create new versions of the methods so that they don't actually do anything
2323
        Y.Object.each(template, function(name){
2324
            if (Y.Lang.isFunction(template[name])){
2325
                mock[name] = function(){
2326
                    Y.Assert.fail("Method " + name + "() was called but was not expected to be.");
2327
                };
2328
            }
2329
        });
2330
        
2331
        //return it
2332
        return mock;    
2333
    };
2334
        
2335
    /**
2336
     * Assigns an expectation to a mock object. This is used to create
2337
     * methods and properties on the mock object that are monitored for
2338
     * calls and changes, respectively.
2339
     * @param {Object} mock The object to add the expectation to.
2340
     * @param {Object} expectation An object defining the expectation. For
2341
     *      a method, the keys "method" and "args" are required with
2342
     *      an optional "returns" key available. For properties, the keys
2343
     *      "property" and "value" are required.
2344
     * @return {void}
2345
     * @method expect
2346
     * @static
2347
     */ 
2348
    Y.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
2349
2350
        //make sure there's a place to store the expectations
2351
        if (!mock.__expectations) {
2352
            mock.__expectations = {};
2353
        }
2354
2355
        //method expectation
2356
        if (expectation.method){
2357
            var name = expectation.method,
2358
                args = expectation.args || expectation.arguments || [],
2359
                result = expectation.returns,
2360
                callCount = Y.Lang.isNumber(expectation.callCount) ? expectation.callCount : 1,
2361
                error = expectation.error,
2362
                run = expectation.run || function(){};
2363
                
2364
            //save expectations
2365
            mock.__expectations[name] = expectation;
2366
            expectation.callCount = callCount;
2367
            expectation.actualCallCount = 0;
2368
                
2369
            //process arguments
2370
            Y.Array.each(args, function(arg, i, array){
2371
                if (!(array[i] instanceof Y.Mock.Value)){
2372
                    array[i] = Y.Mock.Value(Y.Assert.areSame, [arg], "Argument " + i + " of " + name + "() is incorrect.");
2373
                }
2374
            });
2375
        
2376
            //if the method is expected to be called
2377
            if (callCount > 0){
2378
                mock[name] = function(){   
2379
                    expectation.actualCallCount++;
2380
                    Y.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
2381
                    for (var i=0, len=args.length; i < len; i++){
2382
                        if (args[i]){
2383
                            args[i].verify(arguments[i]);
2384
                        } else {
2385
                            Y.Assert.fail("Argument " + i + " (" + arguments[i] + ") was not expected to be used.");
2386
                        }
2387
                        
2388
                    }                
2389
2390
                    run.apply(this, arguments);
2391
                    
2392
                    if (error){
2393
                        throw error;
2394
                    }
2395
                    
2396
                    return result;
2397
                };
2398
            } else {
2399
            
2400
                //method should fail if called when not expected
2401
                mock[name] = function(){
2402
                    Y.Assert.fail("Method " + name + "() should not have been called.");
2403
                };
2404
            }
2405
        } else if (expectation.property){
2406
            //save expectations
2407
            mock.__expectations[name] = expectation;
2408
        }
2409
    };
2410
2411
    /**
2412
     * Verifies that all expectations of a mock object have been met and
2413
     * throws an assertion error if not.
2414
     * @param {Object} mock The object to verify..
2415
     * @return {void}
2416
     * @method verify
2417
     * @static
2418
     */ 
2419
    Y.Mock.verify = function(mock /*:Object*/){    
2420
        Y.Object.each(mock.__expectations, function(expectation){
2421
            if (expectation.method) {
2422
                Y.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
2423
            } else if (expectation.property){
2424
                Y.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value."); 
2425
            }
2426
        });    
2427
    };
2428
2429
    Y.Mock.Value = function(method, args, message){
2430
        if (this instanceof Y.Mock.Value){
2431
            this.verify = function(value){
2432
                args = [].concat(args);
2433
                args.push(value);
2434
                args.push(message);
2435
                method.apply(null, args);
2436
            };
2437
        } else {
2438
            return new Y.Mock.Value(method, args, message);
2439
        }
2440
    };
2441
    
2442
    Y.Mock.Value.Any = Y.Mock.Value(function(){},[]);
2443
    Y.Mock.Value.Boolean = Y.Mock.Value(Y.Assert.isBoolean,[]);
2444
    Y.Mock.Value.Number = Y.Mock.Value(Y.Assert.isNumber,[]);
2445
    Y.Mock.Value.String = Y.Mock.Value(Y.Assert.isString,[]);
2446
    Y.Mock.Value.Object = Y.Mock.Value(Y.Assert.isObject,[]);
2447
    Y.Mock.Value.Function = Y.Mock.Value(Y.Assert.isFunction,[]);
2448
2449
2450
2451
}, '3.0.0pr2' ,{requires:['substitute','event-custom','array','oop','event-target','event-simulate']});