Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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