$(document).ready(function() { module("Object functions (values, extend, isEqual, and so on...)"); test("objects: keys", function() { equals(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); }); test("objects: values", function() { equals(_.values({one : 1, two : 2}).join(', '), '1, 2', 'can extract the values from an object'); }); test("objects: functions", function() { var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact", "compose", "contains", "defer", "delay", "detect", "each", "every", "extend", "filter", "first", "flatten", "foldl", "foldr", "forEach", "functions", "head", "identity", "include", "indexOf", "inject", "intersect", "invoke", "isArguments", "isArray", "isBoolean", "isDate", "isElement", "isEmpty", "isEqual", "isFunction", "isNaN", "isNull", "isNumber", "isRegExp", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max", "memoize", "methods", "min", "mixin", "noConflict", "pluck", "range", "reduce", "reduceRight", "reject", "rest", "select", "size", "some", "sortBy", "sortedIndex", "tail", "tap", "template", "times", "toArray", "uniq", "uniqueId", "values", "without", "wrap", "zip"]; same(expected, _.methods(_), 'provides a sorted list of functions'); var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); }); test("objects: extend", function() { var result; equals(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); equals(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); equals(_.extend({x:'x'}, {a:'b'}).x, 'x', 'properties not in source dont get overriden'); result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); }); test("objects: clone", function() { var moe = {name : 'moe', lucky : [13, 27, 34]}; var clone = _.clone(moe); equals(clone.name, 'moe', 'the clone as the attributes of the original'); clone.name = 'curly'; ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); clone.lucky.push(101); equals(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); }); test("objects: isEqual", function() { var moe = {name : 'moe', lucky : [13, 27, 34]}; var clone = {name : 'moe', lucky : [13, 27, 34]}; ok(moe != clone, 'basic equality between objects is false'); ok(_.isEqual(moe, clone), 'deep equality is true'); ok(_(moe).isEqual(clone), 'OO-style deep equality works'); ok(!_.isEqual(5, NaN), '5 is not equal to NaN'); ok(NaN != NaN, 'NaN is not equal to NaN (native equality)'); ok(NaN !== NaN, 'NaN is not equal to NaN (native identity)'); ok(!_.isEqual(NaN, NaN), 'NaN is not equal to NaN'); ok(_.isEqual(new Date(100), new Date(100)), 'identical dates are equal'); ok(_.isEqual((/hello/ig), (/hello/ig)), 'identical regexes are equal'); ok(!_.isEqual(null, [1]), 'a falsy is never equal to a truthy'); ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), 'object with the same number of undefined keys are not equal'); }); test("objects: isEmpty", function() { ok(!_([1]).isEmpty(), '[1] is not empty'); ok(_.isEmpty([]), '[] is empty'); ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); ok(_.isEmpty({}), '{} is empty'); ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); ok(_.isEmpty(null), 'null is empty'); ok(_.isEmpty(), 'undefined is empty'); ok(_.isEmpty(''), 'the empty string is empty'); ok(!_.isEmpty('moe'), 'but other strings are not'); var obj = {one : 1}; delete obj.one; ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); }); // Setup remote variables for iFrame tests. var iframe = document.createElement('iframe'); jQuery(iframe).appendTo(document.body); var iDoc = iframe.contentDocument || iframe.contentWindow.document; iDoc.write( "" ); iDoc.close(); test("objects: isElement", function() { ok(!_.isElement('div'), 'strings are not dom elements'); ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); ok(_.isElement(iElement), 'even from another frame'); }); test("objects: isArguments", function() { var args = (function(){ return arguments; })(1, 2, 3); ok(!_.isArguments('string'), 'a string is not an arguments object'); ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); ok(_.isArguments(args), 'but the arguments object is an arguments object'); ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); ok(_.isArguments(iArguments), 'even from another frame'); }); test("objects: isArray", function() { ok(!_.isArray(arguments), 'the arguments object is not an array'); ok(_.isArray([1, 2, 3]), 'but arrays are'); ok(_.isArray(iArray), 'even from another frame'); }); test("objects: isString", function() { ok(!_.isString(document.body), 'the document body is not a string'); ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); ok(_.isString(iString), 'even from another frame'); }); test("objects: isNumber", function() { ok(!_.isNumber('string'), 'a string is not a number'); ok(!_.isNumber(arguments), 'the arguments object is not a number'); ok(!_.isNumber(undefined), 'undefined is not a number'); ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); ok(_.isNumber(NaN), 'NaN is a number'); ok(_.isNumber(Infinity), 'Infinity is a number'); ok(_.isNumber(iNumber), 'even from another frame'); }); test("objects: isBoolean", function() { ok(!_.isBoolean(2), 'a number is not a boolean'); ok(!_.isBoolean("string"), 'a string is not a boolean'); ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); ok(!_.isBoolean(undefined), 'undefined is not a boolean'); ok(!_.isBoolean(NaN), 'NaN is not a boolean'); ok(!_.isBoolean(null), 'null is not a boolean'); ok(_.isBoolean(true), 'but true is'); ok(_.isBoolean(false), 'and so is false'); ok(_.isBoolean(iBoolean), 'even from another frame'); }); test("objects: isFunction", function() { ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); ok(!_.isFunction('moe'), 'strings are not functions'); ok(_.isFunction(_.isFunction), 'but functions are'); ok(_.isFunction(iFunction), 'even from another frame'); }); test("objects: isDate", function() { ok(!_.isDate(100), 'numbers are not dates'); ok(!_.isDate({}), 'objects are not dates'); ok(_.isDate(new Date()), 'but dates are'); ok(_.isDate(iDate), 'even from another frame'); }); test("objects: isRegExp", function() { ok(!_.isRegExp(_.identity), 'functions are not RegExps'); ok(_.isRegExp(/identity/), 'but RegExps are'); ok(_.isRegExp(iRegExp), 'even from another frame'); }); test("objects: isNaN", function() { ok(!_.isNaN(undefined), 'undefined is not NaN'); ok(!_.isNaN(null), 'null is not NaN'); ok(!_.isNaN(0), '0 is not NaN'); ok(_.isNaN(NaN), 'but NaN is'); ok(_.isNaN(iNaN), 'even from another frame'); }); test("objects: isNull", function() { ok(!_.isNull(undefined), 'undefined is not null'); ok(!_.isNull(NaN), 'NaN is not null'); ok(_.isNull(null), 'but null is'); ok(_.isNull(iNull), 'even from another frame'); }); test("objects: isUndefined", function() { ok(!_.isUndefined(1), 'numbers are defined'); ok(!_.isUndefined(null), 'null is defined'); ok(!_.isUndefined(false), 'false is defined'); ok(_.isUndefined(), 'nothing is undefined'); ok(_.isUndefined(undefined), 'undefined is undefined'); ok(_.isUndefined(iUndefined), 'even from another frame'); }); test("objects: tap", function() { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); equals(intercepted, 1, "passes tapped object to interceptor"); equals(returned, 1, "returns tapped object"); returned = _([1,2,3]).chain(). map(function(n){ return n * 2; }). max(). tap(interceptor). value(); ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); }); });