~michael.nelson/ubuntu-webcatalog/1267731-import-sca-apps-error

« back to all changes in this revision

Viewing changes to src/webcatalog/static/yui/3.10.3/build/test/test-debug.js

  • Committer: Tarmac
  • Author(s): Stephen Stewart
  • Date: 2013-06-26 09:19:32 UTC
  • mfrom: (184.1.4 ubuntu-global-nav)
  • Revision ID: tarmac-20130626091932-8urtuli368k8p7ds
[r=beuno,jonas-drange] add ubuntu global nav to apps.ubuntu.com

Show diffs side-by-side

added added

removed removed

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