YUI.add('array-test', function (Y) {

var Assert = Y.Assert,

    suite = new Y.Test.Suite('YUI: Array');

Y.Test.Runner._ignoreEmpty = true;

suite.add(new Y.Test.Case({
    name: 'Array tests',
    _should: {
        ignore: {
            testArray: Y.UA.nodejs,
            testTest_dom: Y.UA.nodejs,
            'test Array.test with window' : !Y.config.win,
            'test Array.test with dom': !Y.config.win
        }
    },
    testArray: function () {
        var els   = document.getElementById('tester').getElementsByTagName('span'),
            nodes = Y.all('#tester span');

        Assert.isArray(Y.Array(els), '');
        Assert.areSame(3, Y.Array(els).length);
        Assert.areSame(2, Y.Array(els, 1).length);

        // @TODO NodeList will be wrapped rather than have the items coerced into an
        // array.  If Array.test is adapted to handle NodeLists this could be made to
        // work.
        Assert.isArray(Y.Array(nodes));
        Assert.areSame(1, Y.Array(nodes).length);

        // Y.log('els length:' + els.length);
        // Y.log('a length:' + a.length);
        // Y.log('els tagName:' + els.tagName);
        // Y.log('els alert:' + els.alert);
        // Y.log('els size:' + els.size);
        // Y.log('els array test:' + Y.Array.test(els));

        // Y.Lang.type is broken for HTMLElementCollections in Safari.  isObject
        // returns false because typeof returns function

        // Y.log('els isObject:' + Y.Lang.isObject(els)); // false in Safari
        // Y.log('els type:' + Y.Lang.type(els) + '+'); // object
        // Y.log('els typeof:' + typeof els); // function
        // Y.log('els isFunction:' + Y.Lang.isFunction(els)); // false
        // Y.log('els.call:' + els.call);
        // Y.log('els.apply:' + els.apply);
    },

    testDedupe: function () {
        var array = ['foo', 'bar', 'foo', 'baz', 'foo', 'baz'];
        Assert.isArray(Y.Array.dedupe(array));
        Y.ArrayAssert.itemsAreSame(['foo', 'bar', 'baz'], Y.Array.dedupe(array));

        array = ["constructor", "toString", "hasOwnProperty", "valueOf"];
        Assert.isArray(Y.Array.dedupe(array));
        Y.ArrayAssert.itemsAreSame(array, Y.Array.dedupe(array));

        array = [1, 2, 1, 3, 1, 3];
        Assert.isArray(Y.Array.dedupe(array));
        Y.ArrayAssert.itemsAreSame([1, 2, 3], Y.Array.dedupe(array));
    },

    'dedupe() should work on array-like objects': function () {
        (function () {
            Y.ArrayAssert.itemsAreSame(['a', 'b', 'c'], Y.Array.dedupe(arguments));
        }('a', 'a', 'b', 'c'));
    },

    testEach: function () {
        var calls = 0,
            data  = ['a', 'b', 'c', 'd'],
            obj   = {foo: 'bar'};

        Y.Array.each(data, function (item, index, array) {
            calls += 1;

            Assert.areSame(data[index], item, 'the current item should be passed to the callback');
            Assert.areSame(data, array, 'the array should be passed to the callback');
            Assert.areSame(Y, this, 'the `this` object should default to the YUI instance');
        });

        Assert.areSame(calls, 4, 'the callback should be called 4 times');

        Y.Array.each(data, function () {
            Assert.areSame(obj, this, 'the `this` object should be overridable');
        }, obj);

        Assert.areSame(Y.Array.each, Y.Array.forEach, 'forEach should be an alias for each');
    },

    'each() should handle sparse arrays correctly': function () {
        var calls = 0,
            foo   = Array(5);

        foo.push('foo');

        Y.Array.each(foo, function () {
            calls += 1;
        });

        Assert.areSame(1, calls);
    },

    'each() should work on array-like objects': function () {
        var calls = 0;

        (function () {
            Y.Array.each(arguments, function () {
                calls += 1;
            });
        }('a', 'b', 'c'));

        Assert.areSame(3, calls);
    },

    testHash: function () {
        Assert.isObject(Y.Array.hash(['a', 'b', 'c'], ['foo', 'bar']));

        Y.ObjectAssert.areEqual(
            {a: 'foo', b: 'bar', c: true},
            Y.Array.hash(['a', 'b', 'c'], ['foo', 'bar']),
            'keys should be mapped to values; true is used for missing values'
        );

        Y.ObjectAssert.areEqual(
            {a: true, b: true, c: true},
            Y.Array.hash(['a', 'b', 'c']),
            'the values array is optional'
        );
    },

    'hash() should handle sparse arrays correctly': function () {
        var keys   = ['foo', 'bar'],
            values = ['baz'],
            hash;

        keys.length   = 5;
        values.length = 5;

        hash = Y.Array.hash(keys, values);

        Y.Assert.areSame(2, Y.Object.size(hash));
        Y.Assert.areSame('baz', hash.foo);
        Y.Assert.isTrue(hash.bar);
    },

    'hash() should work on array-like objects': function () {
        (function () {
            Y.ObjectAssert.areEqual({a: true, b: true, c: true}, Y.Array.hash(arguments));
        }('a', 'b', 'c'));
    },

    testIndexOf: function () {
        var data = ['a', 'b', 1, 0, false, null, 'a'];

        Assert.areSame(0, Y.Array.indexOf(data, 'a'), 'should find the first match');
        Assert.areSame(-1, Y.Array.indexOf(data, 'z'), 'should return -1 on no match');
        Assert.areSame(2, Y.Array.indexOf(data, 1), 'should find numbers');
        Assert.areSame(4, Y.Array.indexOf(data, false), 'should perform strict equality checks');
        Assert.areSame(5, Y.Array.indexOf(data, null), 'should find null');
    },

    'indexOf() should support an ES5-compatible fromIndex arg': function () {
        var data = ['a', 'b', 1, 0, false, null, 'a'];

        Assert.areSame(0, Y.Array.indexOf(data, 'a', 0), 'should find the first match');
        Assert.areSame(6, Y.Array.indexOf(data, 'a', 1), 'should find the 6th match');
        Assert.areSame(-1, Y.Array.indexOf(data, 'a', 7), 'should return -1 on no match');
        Assert.areSame(0, Y.Array.indexOf(data, 'a', -8), 'should find the first match');
        Assert.areSame(6, Y.Array.indexOf(data, 'a', -1), 'should find the 6th match');

        Assert.areSame(0, Y.Array.indexOf(data, 'a', 0.1), 'should convert fromIndex to an int');
        Assert.areSame(0, Y.Array.indexOf(data, 'a', -0.1), 'should floor the absolute value of fromIndex to convert to an int');
        Assert.areSame(6, Y.Array.indexOf(data, 'a', '1'), 'should convert string fromIndex to a number');
        Assert.areSame(0, Y.Array.indexOf(data, 'a', undefined), 'should convert undefined fromIndex to a number');
        Assert.areSame(0, Y.Array.indexOf(data, 'a', NaN), 'should treat NaN as 0');
        Assert.areSame(-1, Y.Array.indexOf(data, 'a', 8), 'should return -1 on too-high fromIndex');
        Assert.areSame(-1, Y.Array.indexOf(data, 'a', Infinity), 'should return -1 on Infinity');
        Assert.areSame(0, Y.Array.indexOf(data, 'a', -Infinity), 'should return 0 on negative Infinity');
    },

    'indexOf() should handle sparse arrays correctly': function () {
        var array = ['a', 'b'];
        array[3] = undefined;

        Assert.areSame(3, Y.Array.indexOf(array, undefined));
    },

    'indexOf() should work on array-like objects': function () {
        (function () {
            Assert.areSame(0, Y.Array.indexOf(arguments, 'a'));
            Assert.areSame(1, Y.Array.indexOf(arguments, 'b'));
            Assert.areSame(2, Y.Array.indexOf(arguments, 'c'));
            Assert.areSame(-1, Y.Array.indexOf(arguments, 'd'));
        }('a', 'b', 'c'));
    },

    testNumericSort: function () {
        // the stock sort behavior should fail to produce desired result

        Y.ArrayAssert.itemsAreEqual([1, 100, 2, 3], [3, 100, 1, 2].sort());
        Y.ArrayAssert.itemsAreEqual([1, 2, 3, 100], [3, 100, 1, 2].sort(Y.Array.numericSort));
    },

    testSome: function () {
        var data  = [1, 2, 3],
            obj   = {foo: 'bar'},
            calls = 0;

        Assert.isTrue(Y.Array.some(data, function (v, index, array) {
            calls += 1;

            Assert.areSame(data[index], v, 'the current item should be passed to the callback');
            Assert.areSame(data, array, 'the array should be passed to the callback');
            if (Y.config.win) {
                Assert.areSame(Y.config.win, this, 'the `this` object should default to the global object');
            }

            if (v === 2) { return 'truthy'; }
            if (v === 3) { Y.fail('truthy value did not stop iteration'); }
        }), 'should return true');

        Assert.areSame(2, calls, 'callback should be called twice');

        Assert.isFalse(Y.Array.some(data, function () {
            Assert.areSame(obj, this, 'the `this` object should be overridable');
        }, obj), 'should return false');
    },

    'some() should handle sparse arrays correctly': function () {
        var calls = 0,
            foo   = Array(5);

        foo.push('foo');

        Y.Array.some(foo, function () {
            calls += 1;
        });

        Assert.areSame(1, calls);
    },

    'some() should work on array-like objects': function () {
        var calls = 0;

        (function () {
            Y.Array.some(arguments, function (value) {
                calls += 1;

                if (value === 'b') {
                    return true;
                }
            });
        }('a', 'b', 'c'));

        Assert.areSame(2, calls);
    },

    'test Array.test': function () {
        Y.Assert.areEqual(0, Y.Array.test(function(){})); // functions should fail
        Y.Assert.areEqual(0, Y.Array.test('string'));
        Y.Assert.areEqual(0, Y.Array.test(12345));
        Y.Assert.areEqual(0, Y.Array.test(null));
        Y.Assert.areEqual(0, Y.Array.test(undefined));
        Y.Assert.areEqual(0, Y.Array.test(/regexp/));
        Y.Assert.areEqual(0, Y.Array.test(new Date()));
        Y.Assert.areEqual(1, Y.Array.test([1, 2]));
        Y.Assert.areEqual(1, Y.Array.test([]));
        // Y.Assert.areEqual(1, Y.Array.test('string'.toCharArray()));
        Y.Assert.areEqual(2, Y.Array.test(arguments), 'arguments should be arraylike'); // arguments collection
    },

    'test Array.test with window' : function() {
        Y.Assert.areEqual(0, Y.Array.test(Y.config.win));
    },

    'test Array.test with dom': function() {
        Y.Assert.areEqual(0, Y.Array.test(Y.one('#tester'))); // single nodes should fail
        Y.Assert.areEqual(2, Y.Array.test(document.getElementsByTagName('span')), 'htmlelement collections should be arraylike'); // HTMLElementsCollection

        // @TODO figure out what to do with this.  A NodeList does not contain a collection of Nodes; it
        // contains a collection of HTMLElements.
        // Y.Assert.areEqual(3, Y.Array.test(Y.all('#btnRun')), 'nodelists should be specifically identified as a special collection'); // NodeList
        Y.Assert.areEqual(0, Y.Array.test(Y.all('span')), 'nodelists are not currently considered arraylike'); // NodeList
    }
}));

Y.Test.Runner.add(suite);

}, '@VERSION@', {requires: ['test']});
