UNPKG

sugar

Version:

A Javascript library for working with native objects.

841 lines (633 loc) 31 kB
test('ECMAScript', function () { var arr, count, expected, result, previous, current, fn, reg, obj, Person; arr = []; //arr[4294967295] = 'd'; // This will reset the array in < IE8. Modern browsers will ignore the element. equal(arr.length, 0, 'Array internals will not allow more than a 32bit integer as a key. Anything higher will be ignored'); // Array.isArray // all following calls return true equal(Array.isArray([]), true, 'Array.isArray | empty array'); equal(Array.isArray([1]), true, 'Array.nArray | simple array'); equal(Array.isArray(new Array()), true, 'Array.nArray | new array with constructor'); equal(Array.isArray(Array.prototype), true, 'Array.nArray | Array.prototype'); // Little known fact: Array.prototype is itself an array. // all following calls return false equal(Array.isArray(), false, 'Array.nArray | no param'); equal(Array.isArray({}), false, 'Array.nArray | object'); equal(Array.isArray(null), false, 'Array.nArray | null'); equal(Array.isArray(undefined), false, 'Array.nArray | undefined'); equal(Array.isArray(17), false, 'Array.nArray | number'); equal(Array.isArray("Array"), false, 'Array.nArray | string'); equal(Array.isArray(true), false, 'Array.nArray | true'); equal(Array.isArray(false), false, 'Array.nArray | false'); // Array#forEach arr = ['a','b','c']; raisesError(function(){ [].forEach(); }, 'Array#forEach | should raise an error when no fn given'); result = arr.forEach(function(){ equal(this, nullScope, 'Array#forEach | scope should be undefined when not passed'); }); result = arr.forEach(function(){ equal(this.toString(), 'wasabi', 'Array#forEach | scope can be passed'); }, 'wasabi'); equal(result, undefined, 'Array#forEach | returns undefined'); arr[234] = 'd'; count = 0; expected = ['a','b','c','d']; arr.forEach(function(el, i, arr){ arr.push(3) equal(el, expected[count], 'Array#forEach | elements should be as expected'); equal(typeof i, 'number', 'Array#forEach | i must be a number'); count++; }, 'wasabi'); equal(count, 4, 'Array#forEach | will not visit elements that were added since beginning the loop or visit missing elements'); arr = ['a']; arr[-3] = 'b'; // This will lock browsers, including native implementations. Sparse array // optimizations are NOT in the ECMA spec, it would seem. // arr[4294967294] = 'c'; arr[256] = 'd'; count = 0; arr.forEach(function(el, i){ count++; }); equal(count, 2, 'Array#forEach | will only visit elements with valid indexes'); equal(arr.length, 257, 'Array#forEach | "numerically greater than the name of every property whose name is an array index"'); arr.length = 50; arr.forEach(function() { count++; }); equal(arr[4294967294], undefined, 'Array#forEach | setting the length property will delete all elements above that index'); arr = ['a','b','c']; expected = ['a','x']; count = 0; arr.forEach(function(el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(el, expected[count], 'Array#forEach | elements should be as expected'); count++; }); equal(count, 2, 'Array#forEach | elements deleted after the loop begins should not be visited'); arr = []; expected = ['moo']; count = 0; arr[2] = 'two'; arr['2'] = 'moo'; arr.forEach(function(el, i){ equal(el, expected[count], 'Array#forEach | strings and numbers are both the same for accessing array elements'); count++; }); equal(count, 1, 'Array#forEach | setting array elements with a string is the same as with a number'); arr = []; arr[2] = 'c'; arr[1] = 'b'; arr[0] = 'a'; result = []; arr.forEach(function(el) { result.push(el); }); equal(result, ['a','b','c'], 'Array#forEach | walks array in order'); count = 0; [1,2,3].forEach(function(el) { count++; return false; }); equal(count, 3, 'Array#forEach | returning false will not break the loop'); count = 0; arr = [1,2]; arr.push(undefined); arr.push(3); arr.forEach(function(el) { count++; return false; }); equal(count, 4, 'Array#forEach | undefined members are counted'); // Array#indexOf arr = [1,2,3]; arr[-2] = 4; // Throw a wrench in the gears by assigning a non-valid array index as object property. equal(arr.indexOf(1), 0, 'Array#indexOf | finds 1'); equal(arr.indexOf(1) === 0, true, 'Array#indexOf | finds 1 and is result strictly equal'); equal(arr.indexOf(4), -1, 'Array#indexOf | does not find 4'); equal(arr.indexOf('1'), -1, 'Array#indexOf | Uses strict equality'); equal(arr.indexOf(2, 1), 1, 'Array#indexOf | from index 1'); equal(arr.indexOf(2, 2), -1, 'Array#indexOf | from index 2'); equal(arr.indexOf(2, 3), -1, 'Array#indexOf | from index 3'); equal(arr.indexOf(2, 4), -1, 'Array#indexOf | from index 4'); equal(arr.indexOf(3, -1), 2, 'Array#indexOf | from index -1'); equal(arr.indexOf(3, -2), 2, 'Array#indexOf | from index -2'); equal(arr.indexOf(3, -3), 2, 'Array#indexOf | from index -3'); equal(arr.indexOf(3, -4), 2, 'Array#indexOf | from index -4'); // These tests will by proxy be stress testing the toInteger internal private function. equal(arr.indexOf(1, NaN), 0, 'Array#indexOf | index NaN becomes 0'); equal(arr.indexOf(1, true), -1, 'Array#indexOf | index true becomes 1'); equal(arr.indexOf(1, false), 0, 'Array#indexOf | index false becomes 0'); equal(arr.indexOf(1, 0.1), 0, 'Array#indexOf | index 0.1 becomes 0'); equal(arr.indexOf(1, 1.1), -1, 'Array#indexOf | index 1.1 becomes 1'); equal(arr.indexOf(3, -0.1), 2, 'Array#indexOf | index -0.1 becomes 0'); equal(arr.indexOf(3, -1.1), 2, 'Array#indexOf | index -1.1 becomes -1'); equal(arr.indexOf(1, 1.7), -1, 'Array#indexOf | index 1.7 becomes 1'); equal(arr.indexOf(3, -1.7), 2, 'Array#indexOf | index -1.7 becomes -1'); fn = function(){}; reg = /arf/; obj = { moo: 'cow' }; equal([fn].indexOf(fn), 0, 'Array#indexOf | finds function references'); equal([reg].indexOf(reg), 0, 'Array#indexOf | finds regex references'); equal([obj].indexOf(obj), 0, 'Array#indexOf | finds object references'); arr = []; arr[2] = 'c'; arr[1] = 'c'; arr[0] = 'c'; equal(arr.indexOf('c'), 0, 'Array#indexOf | walks array in order'); equal(Array.prototype.indexOf.call('moo', 'o'), 1, 'Array#indexOf | should work on strings as well'); arr = []; arr[3] = 'a'; equal(arr.indexOf('a'), 3, 'Array#indexOf | must work on sparse arrays as well'); // Although Infinity appears to be allowable in the ECMA spec, both of these cases // would appear to kill all modern browsers. // equal(arr.indexOf(1, Infinity), -1, 'Array#indexOf | infinity is valid'); This locks the browser... should it?? // equal(arr.indexOf(1, -Infinity), 0, 'Array#indexOf | -infinity is valid'); // Array#lastIndexOf arr = ['a', 1, 'a']; arr[-2] = 'a'; // Throw a wrench in the gears by assigning a non-valid array index as object property. equal(arr.lastIndexOf('a'), 2, 'Array#lastIndexOf | finds a'); equal(arr.lastIndexOf('a') === 2, true, 'Array#lastIndexOf | finds a and is result strictly equal'); equal(arr.lastIndexOf('c'), -1, 'Array#lastIndexOf | does not find c'); equal(arr.lastIndexOf('1'), -1, 'Array#lastIndexOf | Uses strict equality'); equal(arr.lastIndexOf('a', 1), 0, 'Array#lastIndexOf | from index 1'); equal(arr.lastIndexOf('a', 2), 2, 'Array#lastIndexOf | from index 2'); equal(arr.lastIndexOf('a', 3), 2, 'Array#lastIndexOf | from index 3'); equal(arr.lastIndexOf('a', 4), 2, 'Array#lastIndexOf | from index 4'); equal(arr.lastIndexOf('a', 0), 0, 'Array#lastIndexOf | from index 0'); equal(arr.lastIndexOf('a', -1), 2, 'Array#lastIndexOf | from index -1'); equal(arr.lastIndexOf('a', -2), 0, 'Array#lastIndexOf | from index -2'); equal(arr.lastIndexOf('a', -3), 0, 'Array#lastIndexOf | from index -3'); equal(arr.lastIndexOf('a', -4), -1, 'Array#lastIndexOf | from index -4'); fn = function(){}; reg = /arf/; obj = { moo: 'cow' }; equal([fn].lastIndexOf(fn), 0, 'Array#lastIndexOf | finds function references'); equal([reg].lastIndexOf(reg), 0, 'Array#lastIndexOf | finds regex references'); equal([obj].lastIndexOf(obj), 0, 'Array#lastIndexOf | finds object references'); arr = []; arr[2] = 'c'; arr[1] = 'c'; arr[0] = 'c'; equal(arr.lastIndexOf('c'), 2, 'Array#lastIndexOf | walks array in order'); equal(Array.prototype.lastIndexOf.call('moo', 'o'), 2, 'Array#lastIndexOf | should work on strings as well'); arr = ['c']; arr[3] = 'a'; equal(arr.lastIndexOf('c'), 0, 'Array#lastIndexOf | must work on sparse arrays as well'); // Array#every raisesError(function(){ [].every(); }, 'Array#every | should raise an error when no first param'); result = arr.every(function(){ equal(this, nullScope, 'Array#every | scope should be undefined when not passed'); }); [1].every(function(){ equal(this.toString(), 'wasabi', 'Array#every | scope can be passed'); }, 'wasabi'); [1].every(function(){ equal(this.toString(), '', 'Array#every | scope can be falsy'); }, ''); equal([].every(function(){ return true; }), true, 'Array#every | empty arrays will always be true'); equal([].every(function(){ return false; }), true, 'Array#every | empty arrays will always be true even when false returned'); equal([1].every(function(){ return 1; }), true, 'Array#every | 1 coerced to true'); equal([1].every(function(){ return 0; }), false, 'Array#every | 0 coerced to false'); equal([1].every(function(){ return 'blah'; }), true, 'Array#every | non-null string coerced to true'); equal([1].every(function(){ return ''; }), false, 'Array#every | blank string coerced to false'); arr = ['c','c','c']; count = 0; result = arr.every(function(el, i, a){ equal(el, 'c', 'Array#every | first argument is element'); equal(i, count, 'Array#every | second argument is index'); equal(a, arr, 'Array#every | third argument is the array'); count++; return el == 'c'; }); equal(result, true, 'Array#every | all are c'); equal(count, 3, 'Array#every | should have been called 3 times'); arr = ['a','b','c']; count = 0; result = arr.every(function(el){ count++; return el == 'c'; }); equal(result, false, 'Array#every | not all are c'); equal(count, 1, 'Array#every | should stop once it can return false'); arr = []; arr[247] = 'a'; count = 0; result = arr.every(function(el){ count++; return el == 'a'; }); equal(result, true, 'Array#every | sparse arrays should not count missing elements'); equal(count, 1, 'Array#every | sparse arrays should have called once only'); arr = ['a','b','c']; expected = ['a','x']; count = 0; arr.every(function(el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(el, expected[count], 'Array#every | elements should be as expected'); count++; return true; }); equal(count, 2, 'Array#every | elements deleted after the loop begins should not be visited'); // Array#some raisesError(function(){ [].some(); }, 'Array#some | should raise an error when no first param'); result = arr.some(function(){ equal(this, nullScope, 'Array#some | scope should be undefined when not passed'); }); [1].some(function(){ equal(this.toString(), 'wasabi', 'Array#some | scope can be passed'); }, 'wasabi'); [1].some(function(){ equal(this.toString(), '', 'Array#some | scope can be falsy'); }, ''); equal([].some(function(){ return true; }), false, 'Array#some | empty arrays will always be false'); equal([].some(function(){ return false; }), false, 'Array#some | empty arrays will always be false even when false returned'); equal([1].some(function(){ return 1; }), true, 'Array#some | 1 coerced to true'); equal([1].some(function(){ return 0; }), false, 'Array#some | 0 coerced to false'); equal([1].some(function(){ return 'blah'; }), true, 'Array#some | non-null string coerced to true'); equal([1].some(function(){ return ''; }), false, 'Array#some | blank string coerced to false'); arr = ['c','c','c']; count = 0; result = arr.some(function(el, i, a){ equal(el, 'c', 'Array#some | first argument is element'); equal(i, count, 'Array#some | second argument is index'); equal(a, arr, 'Array#some | third argument is the array'); count++; return el == 'c'; }); equal(result, true, 'Array#some | some are c'); equal(count, 1, 'Array#some | should stop as soon as it finds an element'); arr = ['a','b','c']; count = 0; result = arr.some(function(el){ count++; return el == 'd'; }); equal(result, false, 'Array#some | none are d'); equal(count, 3, 'Array#some | should have been called 3 times'); arr = []; arr[247] = 'a'; count = 0; result = arr.some(function(el){ count++; return el == 'a'; }); equal(result, true, 'Array#some | sparse arrays should not count missing elements'); equal(count, 1, 'Array#some | sparse arrays should have called once only'); arr = ['a','b','c']; expected = ['a','x']; count = 0; arr.some(function(el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(el, expected[count], 'Array#some | elements should be as expected'); count++; return false; }); equal(count, 2, 'Array#some | elements deleted after the loop begins should not be visited'); // Array#map raisesError(function(){ [].map(); }, 'Array#map | should raise an error when no first param'); result = arr.map(function(){ equal(this, nullScope, 'Array#map | scope should be undefined when not passed'); }); [1].map(function(){ equal(this.toString(), 'wasabi', 'Array#map | scope can be passed'); }, 'wasabi'); [1].map(function(){ equal(this.toString(), '', 'Array#map | scope can be falsy'); }, ''); [1].map(function(){ equal(Number(this), 0, 'Array#map | scope can be a number'); }, 0); arr = ['c','c','c']; count = 0; result = arr.map(function(el, i, a){ equal(el, 'c', 'Array#map | first argument is element'); equal(i, count, 'Array#map | second argument is index'); equal(a, arr, 'Array#map | third argument is the array'); count++; return 'a'; }); equal(result, ['a','a','a'], 'Array#map | mapped to a'); equal(count, 3, 'Array#map | should have run 3 times'); arr = [1,2,3]; count = 0; result = arr.map(function(el){ return Math.pow(el, 2); }); equal(result, [1,4,9], 'Array#map | n^2'); arr = []; arr[247] = 'a'; count = 0; result = arr.map(function(el){ count++; return 'c'; }); equal(result.length, 248, 'Array#map | resulting array should also be sparse if source was'); equal(count, 1, 'Array#map | callback should only have been called once'); arr = ['a','b','c']; expected = ['a','x']; count = 0; arr.map(function(el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(el, expected[count], 'Array#map | elements should be as expected'); count++; }); equal(count, 2, 'Array#map | elements deleted after the loop begins should not be visited'); // Array#filter raisesError(function(){ [].filter(); }, 'Array#filter | should raise an error when no first param'); result = arr.filter(function(){ equal(this, nullScope, 'Array#filter | scope should be undefined when not passed'); }); [1].filter(function(){ equal(this.toString(), 'wasabi', 'Array#filter | scope can be passed'); }, 'wasabi'); [1].filter(function(){ equal(this.toString(), '', 'Array#filter | scope can be falsy'); }, ''); equal([].filter(function(){ return true; }), [], 'Array#filter | empty arrays will always be []'); equal([].filter(function(){ return false; }), [], 'Array#filter | empty arrays will always be [] even when false returned'); equal([1].filter(function(){ return 1; }), [1], 'Array#filter | 1 coerced to true'); equal([1].filter(function(){ return 0; }), [], 'Array#filter | 0 coerced to false'); equal([1].filter(function(){ return 'blah'; }), [1], 'Array#filter | non-null string coerced to true'); equal([1].filter(function(){ return ''; }), [], 'Array#filter | blank string coerced to false'); arr = ['c','c','c']; count = 0; result = arr.filter(function(el, i, a){ equal(el, 'c', 'Array#filter | first argument is element'); equal(i, count, 'Array#filter | second argument is index'); equal(a, arr, 'Array#filter | third argument is the array'); count++; return el == 'c'; }); equal(result, ['c','c','c'], 'Array#filter | filter are c'); equal(count, 3, 'Array#filter | should have executed 3 times'); arr = ['a','b','c']; count = 0; result = arr.filter(function(el){ count++; return el == 'b'; }); equal(result, ['b'], 'Array#filter | returns [b]'); equal(count, 3, 'Array#filter | should have been called 3 times'); arr = []; arr[247] = 'a'; count = 0; result = arr.filter(function(el){ count++; return true; }); equal(result, ['a'], 'Array#filter | sparse arrays should not count missing elements'); equal(count, 1, 'Array#filter | sparse arrays should have called once only'); arr = ['a','b','c']; expected = ['a','x']; count = 0; result = arr.filter(function(el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(el, expected[count], 'Array#filter | elements should be as expected'); count++; return true; }); equal(result, ['a','x'], 'Array#filter | modified array should appear as the result'); equal(count, 2, 'Array#filter | elements deleted after the loop begins should not be visited'); // Array#reduce raisesError(function(){ [1].reduce(); }, 'Array#reduce | should raise an error when no callback provided'); raisesError(function(){ [].reduce(function(){}); }, 'Array#reduce | should raise an error on an empty array with no initial value'); [1].reduce(function(){ equal(this, nullScope, 'Array#reduce | scope should be undefined'); }, 1); arr = [1,2,3]; previous = [1,3]; current = [2,3]; count = 0; result = arr.reduce(function(prev, el, i, o){ equal(prev, previous[count], 'Array#reduce | first argument is the prev value'); equal(el, current[count], 'Array#reduce | second argument is element'); equal(i, count + 1, 'Array#reduce | third argument is index'); equal(o, arr, 'Array#reduce | fourth argument is the array'); count++; return prev + el; }); equal(result, 6, 'Array#reduce | result is correct'); equal(count, 2, 'Array#reduce | should have been called 3 times'); equal([1].reduce(function(){ return 324242; }), 1, 'Array#reduce | function is not called and returns 1'); count = 0; [1].reduce(function(prev, current, i) { equal(prev, 5, 'Array#reduce | prev is equal to the inital value if it is provided'); equal(current, 1, 'Array#reduce | current is equal to the first value in the array if no intial value provided'); equal(i, 0, 'Array#reduce | i is 0 when an initial value is passed'); count++; }, 5); equal(count, 1, 'Array#reduce | should have been called once'); arr = ['a','b','c']; previous = ['a','ab']; current = ['b','c']; count = 0; result = arr.reduce(function(prev, el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(prev, previous[count], 'Array#reduce | previous should be as expected'); equal(el, current[count], 'Array#reduce | current should be as expected'); count++; return prev + el; }); equal(count, 2, 'Array#reduce | elements deleted after the loop begins should not be visited'); equal([1,2,3].reduce(function(a, n){ return a + n; }, 0), 6, 'Array#reduce | can handle initial value of 0'); // Array#reduceRight raisesError(function(){ [1].reduceRight(); }, 'Array#reduceRight | should raise an error when no callback provided'); raisesError(function(){ [].reduceRight(function(){}); }, 'Array#reduceRight | should raise an error on an empty array with no initial value'); [1].reduceRight(function(){ equal(this, nullScope, 'Array#reduceRight | scope should be undefined'); }, 1); arr = [1,2,3]; previous = [3,5]; current = [2,1]; count = 0; result = arr.reduceRight(function(prev, el, i, o){ equal(prev, previous[count], 'Array#reduceRight | first argument is the prev value'); equal(el, current[count], 'Array#reduceRight | second argument is element'); equal(i, 1 - count, 'Array#reduceRight | third argument is index'); equal(o, arr, 'Array#reduceRight | fourth argument is the array'); count++; return prev + el; }); equal(result, 6, 'Array#reduceRight | result is correct'); equal(count, 2, 'Array#reduceRight | should have been called 3 times'); equal([1].reduceRight(function(){ return 324242; }), 1, 'Array#reduceRight | function is not called and returns 1'); count = 0; [1].reduceRight(function(prev, current, i) { equal(prev, 5, 'Array#reduceRight | prev is equal to the inital value if it is provided'); equal(current, 1, 'Array#reduceRight | current is equal to the first value in the array if no intial value provided'); equal(i, 0, 'Array#reduceRight | i is 0 when an initial value is passed'); count++; }, 5); equal(count, 1, 'Array#reduceRight | should have been called once'); arr = ['a','b','c']; previous = ['c','cb']; current = ['b','a']; count = 0; result = arr.reduceRight(function(prev, el, i){ if(i == 0) { arr[1] = 'x'; delete arr[2]; } equal(prev, previous[count], 'Array#reduceRight | previous should be as expected'); equal(el, current[count], 'Array#reduceRight | current should be as expected'); count++; return prev + el; }); equal(count, 2, 'Array#reduceRight | elements deleted after the loop begins should not be visited'); equal([1,2,3].reduceRight(function(a, n){ return a + n; }, 0), 6, 'Array#reduceRight | can handle initial value of 0'); // Polyfills can be run on objects that inherit from Arrays var Soup = function(){}; Soup.prototype = [1,2,3]; var x = new Soup(); count = 0; x.every(function() { count++; return true; }); x.some(function() { count++; }); x.map(function() { count++; }); x.filter(function() { count++; }); x.forEach(function() { count++; }); x.reduce(function() { count++; }); x.reduceRight(function() { count++; }); equal(count, 19, 'Array | array elements in the prototype chain are also properly iterated'); equal(x.indexOf(2), 1, 'Array | indexOf | array elements in the prototype chain are also properly iterated'); equal(x.lastIndexOf(2), 1, 'Array | lastIndexOf | array elements in the prototype chain are also properly iterated'); // String#trim var whiteSpace = '\u0009\u000B\u000C\u0020\u00A0\uFEFF\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000'; var lineTerminators = '\u000A\u000D\u2028\u2029'; equal(whiteSpace.trim(), '', 'String#trim | should trim all WhiteSpace characters defined in 7.2 and Unicode "space, separator and Unicode "space, separator""'); equal(lineTerminators.trim(), '', 'String#trim | should trim all LineTerminator characters defined in 7.3'); // String#trimLeft (non standard) equal(whiteSpace.trimLeft(), '', 'String#trimLeft | should trim all WhiteSpace characters defined in 7.2 and Unicode "space, separator"'); equal(lineTerminators.trimLeft(), '', 'String#trimLeft | should trim all LineTerminator characters defined in 7.3'); // String#trimRight (non standard) equal(whiteSpace.trimRight(), '', 'String#trimRight | should trim all WhiteSpace characters defined in 7.2 and Unicode "space, separator"'); equal(lineTerminators.trimRight(), '', 'String#trimRight | should trim all LineTerminator characters defined in 7.3'); equal(String.prototype.trim.call([1]), '1', 'String#trim | should handle objects as well'); // Object#keys raisesError(function(){ Object.keys(undefined); }, 'Object#keys | raises a TypeError for undefined'); raisesError(function(){ Object.keys(null); }, 'Object#keys | raises a TypeError for null'); raisesError(function(){ Object.keys(true); }, 'Object#keys | raises a TypeError for booleans'); raisesError(function(){ Object.keys(NaN); }, 'Object#keys | raises a TypeError for NaN'); raisesError(function(){ Object.keys(3); }, 'Object#keys | raises a TypeError for numbers'); raisesError(function(){ Object.keys('moofa'); }, 'Object#keys | raises a TypeError for strings'); equal(Object.keys({ moo:'bar', broken:'wear' }), ['moo','broken'], 'Object#keys | returns keys of an object'); equal(Object.keys(['a','b','c']), ['0','1','2'], 'Object#keys | returns indexes of an array'); equal(Object.keys(/foobar/), [], 'Object#keys | regexes return a blank array'); equal(Object.keys(function(){}), [], 'Object#keys | functions return a blank array'); equal(Object.keys(new Date), [], 'Object#keys | dates return a blank array'); Person = function() { this.broken = 'wear'; }; Person.prototype = { cat: 'dog' }; equal(Object.keys(new Person), ['broken'], 'Object#keys | will get instance properties but not inherited properties'); // Date.now (support may be missing if only core is included) if(Date.now) { equalWithMargin(Date.now(), new Date().getTime(), 5, 'Date#now | basic functionality'); } // Date.parse // Returns 807937200000 in time zone GMT-0300, and other values in other // timezones, since the argument does not specify a time zone. equal(Date.parse("Aug 9, 1995"), new Date(1995, 7, 9).getTime(), 'Date#parse | No timezone'); // Returns 807926400000 no matter the local time zone. equal(Date.parse("Wed, 09 Aug 1995 00:00:00 GMT"), new Date(807926400000).getTime(), 'Date#parse | GMT'); // Returns 807937200000 in timezone GMT-0300, and other values in other // timezones, since there is no time zone specifier in the argument. equal(Date.parse("Wed, 09 Aug 1995 00:00:00"), new Date(1995, 7, 9).getTime(), 'Date#parse | No timezone with time'); equal(Date.parse("Thu, 09 Aug 1995 00:00:00 GMT-0400"), 807940800000, 'Date#parse | 1995/7/9 GMT-04:00'); // Returns 0 no matter the local time zone. equal(Date.parse("Thu, 01 Jan 1970 00:00:00 GMT"), 0, 'Date#parse | 1970/1/1 GMT'); // Note: Avoiding non GMT dates around the epoch as they tend to be unreliable. // Returns 14400000 in timezone GMT-0400, and other values in other // timezones, since there is no time zone specifier in the argument. // equal(Date.parse("Thu, 01 Jan 1970 00:00:00"), (new Date).getTimezoneOffset().minutes(), 'Date#parse | 1970/1/1 Local'); // Returns 14400000 no matter the local time zone. // equal(Date.parse("Thu, 01 Jan 1970 00:00:00 GMT-0400"), new Date(1995, 7, 9).getTime(), 'Date#parse | 1970/1/1 GMT-04:00'); if(Date.prototype.toISOString) { // Date#toISOString (support may be missing if only core is included) equal(new Date(Date.UTC(2000, 0, 1)).toISOString(), '2000-01-01T00:00:00.000Z', 'Date#toISOString | new millenium!'); equal(new Date(Date.UTC(1978, 7, 25)).toISOString(), '1978-08-25T00:00:00.000Z', 'Date#toISOString | happy birthday!'); equal(new Date(Date.UTC(1978, 7, 25, 11, 45, 33, 456)).toISOString(), '1978-08-25T11:45:33.456Z', 'Date#toISOString | with time'); // Date#toJSON // Essentially just an ISO string. Add more tests as needed. equal(new Date(2002, 7, 25).toJSON(), new Date(2002, 7, 25).toISOString(), 'Date#toJSON | output'); } // Function#bind var instance, BoundPerson; raisesError(function(){ Function.prototype.bind.call('mooo'); }, 'Function#bind | Raises an error when used on anything un-callable'); raisesError(function(){ Function.prototype.bind.call(/mooo/); }, 'Function#bind | Regexes are functions in chrome'); equal((function(){ return this; }).bind('yellow')().toString(), 'yellow', 'Function#bind | basic binding of this arg'); equal((function(){ return arguments[0]; }).bind('yellow', 'mellow')(), 'mellow', 'Function#bind | currying argument 1'); equal((function(){ return arguments[1]; }).bind('yellow', 'mellow', 'fellow')(), 'fellow', 'Function#bind | currying argument 2'); equal((function(){ return this; }).bind(undefined)(), nullScope, 'Function#bind | passing undefined as the scope'); (function(a, b){ equal(this.toString(), 'yellow', 'Function#bind | ensure only one call | this object'); equal(a, 'mellow', 'Function#bind | ensure only one call | argument 1'); equal(b, 'fellow', 'Function#bind | ensure only one call | argument 2'); }).bind('yellow', 'mellow', 'fellow')(); // It seems this functionality can't be achieved in a JS polyfill... // equal((function(){}).bind().prototype, undefined, 'Function#bind | currying argument 2'); Person = function(a, b) { this.first = a; this.second = b; }; BoundPerson = Person.bind({ mellow: 'yellow' }, 'jump'); instance = new BoundPerson('jumpy'); equal(instance.mellow, undefined, 'Function#bind | passed scope is ignored when used with the new keyword'); equal(instance.first, 'jump', 'Function#bind | curried argument makes it to the constructor'); equal(instance.second, 'jumpy', 'Function#bind | argument passed to the constructor makes it in as the second arg'); equal(instance instanceof Person, true, 'Function#bind | instance of the class'); // Node has a discrepancy in its implementation of instanceof, where it will throw a // TypeError when the RHS has no prototype object. Looking at the spec it appears that // this may in fact be correct behavior, but no other environment does this. Also note // that the ONLY way I can see to create a function (instanceof will throw an error in // any case on anything else) with no prototype is through the bind method. All other // environments properly have an undefined prototype, but do not raise an error on instanceof. skipEnvironments(['node'], function() { equal(instance instanceof BoundPerson, true, 'Function#bind | instance of the bound class'); // Note that this spec appears to be wrong in the MDN docs: // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind // Changing this test to assert true as native implementations all function this way. equal(new Person() instanceof BoundPerson, true, 'Function#bind | instance of unbound class is not an instance of the bound class'); }); // Binding functions without a prototype should not explode. Object.prototype.toString.bind('hooha')(); // Ensure that all prototype methods affected by Sugar are still overwriteable. var storedEach = Array.prototype.each; var a = []; a.each = 'OH PLEASE'; equal(a.each, 'OH PLEASE', 'Sugar methods can ALL be overwritten!'); Array.prototype.each = storedEach; });