~veebers/sloecode/jquery-changeover

« back to all changes in this revision

Viewing changes to sloecode/public/yui/3.3.0/build/test/test-debug.js

  • Committer: Christopher Lee
  • Date: 2012-03-03 05:04:34 UTC
  • Revision ID: veebers@gmail.com-20120303050434-smeo0c6n7gz53thy
Removed unused YUI javascript (still using css libraries)

Show diffs side-by-side

added added

removed removed

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