1'use strict'; 2 3var ES = require('../').ES5; 4var test = require('tape'); 5 6var forEach = require('foreach'); 7var is = require('object-is'); 8 9var coercibleObject = { valueOf: function () { return '3'; }, toString: function () { return 42; } }; 10var coercibleFnObject = { 11 valueOf: function () { return function valueOfFn() {}; }, 12 toString: function () { return 42; } 13}; 14var valueOfOnlyObject = { valueOf: function () { return 4; }, toString: function () { return {}; } }; 15var toStringOnlyObject = { valueOf: function () { return {}; }, toString: function () { return 7; } }; 16var uncoercibleObject = { valueOf: function () { return {}; }, toString: function () { return {}; } }; 17var uncoercibleFnObject = { 18 valueOf: function () { return function valueOfFn() {}; }, 19 toString: function () { return function toStrFn() {}; } 20}; 21var objects = [{}, coercibleObject, toStringOnlyObject, valueOfOnlyObject]; 22var numbers = [0, -0, Infinity, -Infinity, 42]; 23var nonNullPrimitives = [true, false, 'foo', ''].concat(numbers); 24var primitives = [undefined, null].concat(nonNullPrimitives); 25 26test('ToPrimitive', function (t) { 27 t.test('primitives', function (st) { 28 var testPrimitive = function (primitive) { 29 st.ok(is(ES.ToPrimitive(primitive), primitive), primitive + ' is returned correctly'); 30 }; 31 forEach(primitives, testPrimitive); 32 st.end(); 33 }); 34 35 t.test('objects', function (st) { 36 st.equal(ES.ToPrimitive(coercibleObject), coercibleObject.valueOf(), 'coercibleObject coerces to valueOf'); 37 st.equal(ES.ToPrimitive(coercibleObject, Number), coercibleObject.valueOf(), 'coercibleObject with hint Number coerces to valueOf'); 38 st.equal(ES.ToPrimitive(coercibleObject, String), coercibleObject.toString(), 'coercibleObject with hint String coerces to toString'); 39 st.equal(ES.ToPrimitive(coercibleFnObject), coercibleFnObject.toString(), 'coercibleFnObject coerces to toString'); 40 st.equal(ES.ToPrimitive(toStringOnlyObject), toStringOnlyObject.toString(), 'toStringOnlyObject returns toString'); 41 st.equal(ES.ToPrimitive(valueOfOnlyObject), valueOfOnlyObject.valueOf(), 'valueOfOnlyObject returns valueOf'); 42 st.equal(ES.ToPrimitive({}), '[object Object]', '{} with no hint coerces to Object#toString'); 43 st.equal(ES.ToPrimitive({}, String), '[object Object]', '{} with hint String coerces to Object#toString'); 44 st.equal(ES.ToPrimitive({}, Number), '[object Object]', '{} with hint Number coerces to Object#toString'); 45 st['throws'](function () { return ES.ToPrimitive(uncoercibleObject); }, TypeError, 'uncoercibleObject throws a TypeError'); 46 st['throws'](function () { return ES.ToPrimitive(uncoercibleFnObject); }, TypeError, 'uncoercibleFnObject throws a TypeError'); 47 st.end(); 48 }); 49 50 t.end(); 51}); 52 53test('ToBoolean', function (t) { 54 t.equal(false, ES.ToBoolean(undefined), 'undefined coerces to false'); 55 t.equal(false, ES.ToBoolean(null), 'null coerces to false'); 56 t.equal(false, ES.ToBoolean(false), 'false returns false'); 57 t.equal(true, ES.ToBoolean(true), 'true returns true'); 58 forEach([0, -0, NaN], function (falsyNumber) { 59 t.equal(false, ES.ToBoolean(falsyNumber), 'falsy number ' + falsyNumber + ' coerces to false'); 60 }); 61 forEach([Infinity, 42, 1, -Infinity], function (truthyNumber) { 62 t.equal(true, ES.ToBoolean(truthyNumber), 'truthy number ' + truthyNumber + ' coerces to true'); 63 }); 64 t.equal(false, ES.ToBoolean(''), 'empty string coerces to false'); 65 t.equal(true, ES.ToBoolean('foo'), 'nonempty string coerces to true'); 66 forEach(objects, function (obj) { 67 t.equal(true, ES.ToBoolean(obj), 'object coerces to true'); 68 }); 69 t.equal(true, ES.ToBoolean(uncoercibleObject), 'uncoercibleObject coerces to true'); 70 t.end(); 71}); 72 73test('ToNumber', function (t) { 74 t.ok(is(NaN, ES.ToNumber(undefined)), 'undefined coerces to NaN'); 75 t.ok(is(ES.ToNumber(null), 0), 'null coerces to +0'); 76 t.ok(is(ES.ToNumber(false), 0), 'false coerces to +0'); 77 t.equal(1, ES.ToNumber(true), 'true coerces to 1'); 78 t.ok(is(NaN, ES.ToNumber(NaN)), 'NaN returns itself'); 79 forEach([0, -0, 42, Infinity, -Infinity], function (num) { 80 t.equal(num, ES.ToNumber(num), num + ' returns itself'); 81 }); 82 forEach(['foo', '0', '4a', '2.0', 'Infinity', '-Infinity'], function (numString) { 83 t.ok(is(+numString, ES.ToNumber(numString)), '"' + numString + '" coerces to ' + Number(numString)); 84 }); 85 forEach(objects, function (object) { 86 t.ok(is(ES.ToNumber(object), ES.ToNumber(ES.ToPrimitive(object))), 'object ' + object + ' coerces to same as ToPrimitive of object does'); 87 }); 88 t['throws'](function () { return ES.ToNumber(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 89 t.end(); 90}); 91 92test('ToInteger', function (t) { 93 t.ok(is(0, ES.ToInteger(NaN)), 'NaN coerces to +0'); 94 forEach([0, Infinity, 42], function (num) { 95 t.ok(is(num, ES.ToInteger(num)), num + ' returns itself'); 96 t.ok(is(-num, ES.ToInteger(-num)), '-' + num + ' returns itself'); 97 }); 98 t.equal(3, ES.ToInteger(Math.PI), 'pi returns 3'); 99 t['throws'](function () { return ES.ToInteger(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 100 t.end(); 101}); 102 103test('ToInt32', function (t) { 104 t.ok(is(0, ES.ToInt32(NaN)), 'NaN coerces to +0'); 105 forEach([0, Infinity], function (num) { 106 t.ok(is(0, ES.ToInt32(num)), num + ' returns +0'); 107 t.ok(is(0, ES.ToInt32(-num)), '-' + num + ' returns +0'); 108 }); 109 t['throws'](function () { return ES.ToInt32(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 110 t.ok(is(ES.ToInt32(0x100000000), 0), '2^32 returns +0'); 111 t.ok(is(ES.ToInt32(0x100000000 - 1), -1), '2^32 - 1 returns -1'); 112 t.ok(is(ES.ToInt32(0x80000000), -0x80000000), '2^31 returns -2^31'); 113 t.ok(is(ES.ToInt32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); 114 forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { 115 t.ok(is(ES.ToInt32(num), ES.ToInt32(ES.ToUint32(num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for 0x' + num.toString(16)); 116 t.ok(is(ES.ToInt32(-num), ES.ToInt32(ES.ToUint32(-num))), 'ToInt32(x) === ToInt32(ToUint32(x)) for -0x' + num.toString(16)); 117 }); 118 t.end(); 119}); 120 121test('ToUint32', function (t) { 122 t.ok(is(0, ES.ToUint32(NaN)), 'NaN coerces to +0'); 123 forEach([0, Infinity], function (num) { 124 t.ok(is(0, ES.ToUint32(num)), num + ' returns +0'); 125 t.ok(is(0, ES.ToUint32(-num)), '-' + num + ' returns +0'); 126 }); 127 t['throws'](function () { return ES.ToUint32(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 128 t.ok(is(ES.ToUint32(0x100000000), 0), '2^32 returns +0'); 129 t.ok(is(ES.ToUint32(0x100000000 - 1), 0x100000000 - 1), '2^32 - 1 returns 2^32 - 1'); 130 t.ok(is(ES.ToUint32(0x80000000), 0x80000000), '2^31 returns 2^31'); 131 t.ok(is(ES.ToUint32(0x80000000 - 1), 0x80000000 - 1), '2^31 - 1 returns 2^31 - 1'); 132 forEach([0, Infinity, NaN, 0x100000000, 0x80000000, 0x10000, 0x42], function (num) { 133 t.ok(is(ES.ToUint32(num), ES.ToUint32(ES.ToInt32(num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for 0x' + num.toString(16)); 134 t.ok(is(ES.ToUint32(-num), ES.ToUint32(ES.ToInt32(-num))), 'ToUint32(x) === ToUint32(ToInt32(x)) for -0x' + num.toString(16)); 135 }); 136 t.end(); 137}); 138 139test('ToUint16', function (t) { 140 t.ok(is(0, ES.ToUint16(NaN)), 'NaN coerces to +0'); 141 forEach([0, Infinity], function (num) { 142 t.ok(is(0, ES.ToUint16(num)), num + ' returns +0'); 143 t.ok(is(0, ES.ToUint16(-num)), '-' + num + ' returns +0'); 144 }); 145 t['throws'](function () { return ES.ToUint16(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 146 t.ok(is(ES.ToUint16(0x100000000), 0), '2^32 returns +0'); 147 t.ok(is(ES.ToUint16(0x100000000 - 1), 0x10000 - 1), '2^32 - 1 returns 2^16 - 1'); 148 t.ok(is(ES.ToUint16(0x80000000), 0), '2^31 returns +0'); 149 t.ok(is(ES.ToUint16(0x80000000 - 1), 0x10000 - 1), '2^31 - 1 returns 2^16 - 1'); 150 t.ok(is(ES.ToUint16(0x10000), 0), '2^16 returns +0'); 151 t.ok(is(ES.ToUint16(0x10000 - 1), 0x10000 - 1), '2^16 - 1 returns 2^16 - 1'); 152 t.end(); 153}); 154 155test('ToString', function (t) { 156 t['throws'](function () { return ES.ToString(uncoercibleObject); }, TypeError, 'uncoercibleObject throws'); 157 t.end(); 158}); 159 160test('ToObject', function (t) { 161 t['throws'](function () { return ES.ToObject(undefined); }, TypeError, 'undefined throws'); 162 t['throws'](function () { return ES.ToObject(null); }, TypeError, 'null throws'); 163 forEach(numbers, function (number) { 164 var obj = ES.ToObject(number); 165 t.equal(typeof obj, 'object', 'number ' + number + ' coerces to object'); 166 t.equal(true, obj instanceof Number, 'object of ' + number + ' is Number object'); 167 t.ok(is(obj.valueOf(), number), 'object of ' + number + ' coerces to ' + number); 168 }); 169 t.end(); 170}); 171 172test('CheckObjectCoercible', function (t) { 173 t['throws'](function () { return ES.CheckObjectCoercible(undefined); }, TypeError, 'undefined throws'); 174 t['throws'](function () { return ES.CheckObjectCoercible(null); }, TypeError, 'null throws'); 175 var checkCoercible = function (value) { 176 t.doesNotThrow(function () { return ES.CheckObjectCoercible(value); }, '"' + value + '" does not throw'); 177 }; 178 forEach(objects.concat(nonNullPrimitives), checkCoercible); 179 t.end(); 180}); 181 182test('IsCallable', function (t) { 183 t.equal(true, ES.IsCallable(function () {}), 'function is callable'); 184 var nonCallables = [/a/g, {}, Object.prototype, NaN].concat(primitives); 185 forEach(nonCallables, function (nonCallable) { 186 t.equal(false, ES.IsCallable(nonCallable), nonCallable + ' is not callable'); 187 }); 188 t.end(); 189}); 190 191test('SameValue', function (t) { 192 t.equal(true, ES.SameValue(NaN, NaN), 'NaN is SameValue as NaN'); 193 t.equal(false, ES.SameValue(0, -0), '+0 is not SameValue as -0'); 194 forEach(objects.concat(primitives), function (val) { 195 t.equal(val === val, ES.SameValue(val, val), '"' + val + '" is SameValue to itself'); 196 }); 197 t.end(); 198}); 199 200test('Type', function (t) { 201 t.equal(ES.Type(), 'Undefined', 'Type() is Undefined'); 202 t.equal(ES.Type(undefined), 'Undefined', 'Type(undefined) is Undefined'); 203 t.equal(ES.Type(null), 'Null', 'Type(null) is Null'); 204 t.equal(ES.Type(true), 'Boolean', 'Type(true) is Boolean'); 205 t.equal(ES.Type(false), 'Boolean', 'Type(false) is Boolean'); 206 t.equal(ES.Type(0), 'Number', 'Type(0) is Number'); 207 t.equal(ES.Type(NaN), 'Number', 'Type(NaN) is Number'); 208 t.equal(ES.Type('abc'), 'String', 'Type("abc") is String'); 209 t.equal(ES.Type(function () {}), 'Object', 'Type(function () {}) is Object'); 210 t.equal(ES.Type({}), 'Object', 'Type({}) is Object'); 211 t.end(); 212}); 213 214var bothDescriptor = function () { 215 return { '[[Get]]': function () {}, '[[Value]]': true }; 216}; 217var accessorDescriptor = function () { 218 return { 219 '[[Get]]': function () {}, 220 '[[Enumerable]]': true, 221 '[[Configurable]]': true 222 }; 223}; 224var mutatorDescriptor = function () { 225 return { 226 '[[Set]]': function () {}, 227 '[[Enumerable]]': true, 228 '[[Configurable]]': true 229 }; 230}; 231var dataDescriptor = function () { 232 return { 233 '[[Value]]': 42, 234 '[[Writable]]': false, 235 '[[Configurable]]': false 236 }; 237}; 238var genericDescriptor = function () { 239 return { 240 '[[Configurable]]': true, 241 '[[Enumerable]]': false 242 }; 243}; 244 245test('IsPropertyDescriptor', function (t) { 246 forEach(primitives, function (primitive) { 247 t.equal(ES.IsPropertyDescriptor(primitive), false, primitive + ' is not a Property Descriptor'); 248 }); 249 250 t.equal(ES.IsPropertyDescriptor({ invalid: true }), false, 'invalid keys not allowed on a Property Descriptor'); 251 252 t.equal(ES.IsPropertyDescriptor({}), true, 'empty object is an incomplete Property Descriptor'); 253 254 t.equal(ES.IsPropertyDescriptor(accessorDescriptor()), true, 'accessor descriptor is a Property Descriptor'); 255 t.equal(ES.IsPropertyDescriptor(mutatorDescriptor()), true, 'mutator descriptor is a Property Descriptor'); 256 t.equal(ES.IsPropertyDescriptor(dataDescriptor()), true, 'data descriptor is a Property Descriptor'); 257 t.equal(ES.IsPropertyDescriptor(genericDescriptor()), true, 'generic descriptor is a Property Descriptor'); 258 259 t['throws'](function () { 260 ES.IsPropertyDescriptor(bothDescriptor()); 261 }, TypeError, 'a Property Descriptor can not be both a Data and an Accessor Descriptor'); 262 263 t.end(); 264}); 265 266test('IsAccessorDescriptor', function (t) { 267 forEach(nonNullPrimitives.concat(null), function (primitive) { 268 t['throws'](function () { ES.IsAccessorDescriptor(primitive); }, TypeError, primitive + ' is not a Property Descriptor'); 269 }); 270 271 t.equal(ES.IsAccessorDescriptor(), false, 'no value is not an Accessor Descriptor'); 272 t.equal(ES.IsAccessorDescriptor(undefined), false, 'undefined value is not an Accessor Descriptor'); 273 274 t.equal(ES.IsAccessorDescriptor(accessorDescriptor()), true, 'accessor descriptor is an Accessor Descriptor'); 275 t.equal(ES.IsAccessorDescriptor(mutatorDescriptor()), true, 'mutator descriptor is an Accessor Descriptor'); 276 t.equal(ES.IsAccessorDescriptor(dataDescriptor()), false, 'data descriptor is not an Accessor Descriptor'); 277 t.equal(ES.IsAccessorDescriptor(genericDescriptor()), false, 'generic descriptor is not an Accessor Descriptor'); 278 279 t.end(); 280}); 281 282test('IsDataDescriptor', function (t) { 283 forEach(nonNullPrimitives.concat(null), function (primitive) { 284 t['throws'](function () { ES.IsDataDescriptor(primitive); }, TypeError, primitive + ' is not a Property Descriptor'); 285 }); 286 287 t.equal(ES.IsDataDescriptor(), false, 'no value is not a Data Descriptor'); 288 t.equal(ES.IsDataDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); 289 290 t.equal(ES.IsDataDescriptor(accessorDescriptor()), false, 'accessor descriptor is not a Data Descriptor'); 291 t.equal(ES.IsDataDescriptor(mutatorDescriptor()), false, 'mutator descriptor is not a Data Descriptor'); 292 t.equal(ES.IsDataDescriptor(dataDescriptor()), true, 'data descriptor is a Data Descriptor'); 293 t.equal(ES.IsDataDescriptor(genericDescriptor()), false, 'generic descriptor is not a Data Descriptor'); 294 295 t.end(); 296}); 297 298test('IsGenericDescriptor', function (t) { 299 forEach(nonNullPrimitives.concat(null), function (primitive) { 300 t['throws']( 301 function () { ES.IsGenericDescriptor(primitive); }, 302 TypeError, 303 primitive + ' is not a Property Descriptor' 304 ); 305 }); 306 307 t.equal(ES.IsGenericDescriptor(), false, 'no value is not a Data Descriptor'); 308 t.equal(ES.IsGenericDescriptor(undefined), false, 'undefined value is not a Data Descriptor'); 309 310 t.equal(ES.IsGenericDescriptor(accessorDescriptor()), false, 'accessor descriptor is not a generic Descriptor'); 311 t.equal(ES.IsGenericDescriptor(mutatorDescriptor()), false, 'mutator descriptor is not a generic Descriptor'); 312 t.equal(ES.IsGenericDescriptor(dataDescriptor()), false, 'data descriptor is not a generic Descriptor'); 313 314 t.equal(ES.IsGenericDescriptor(genericDescriptor()), true, 'generic descriptor is a generic Descriptor'); 315 316 t.end(); 317}); 318 319test('FromPropertyDescriptor', function (t) { 320 t.equal(ES.FromPropertyDescriptor(), undefined, 'no value begets undefined'); 321 t.equal(ES.FromPropertyDescriptor(undefined), undefined, 'undefined value begets undefined'); 322 323 forEach(nonNullPrimitives.concat(null), function (primitive) { 324 t['throws']( 325 function () { ES.FromPropertyDescriptor(primitive); }, 326 TypeError, 327 primitive + ' is not a Property Descriptor' 328 ); 329 }); 330 331 var accessor = accessorDescriptor(); 332 t.deepEqual(ES.FromPropertyDescriptor(accessor), { 333 get: accessor['[[Get]]'], 334 set: accessor['[[Set]]'], 335 enumerable: !!accessor['[[Enumerable]]'], 336 configurable: !!accessor['[[Configurable]]'] 337 }); 338 339 var mutator = mutatorDescriptor(); 340 t.deepEqual(ES.FromPropertyDescriptor(mutator), { 341 get: mutator['[[Get]]'], 342 set: mutator['[[Set]]'], 343 enumerable: !!mutator['[[Enumerable]]'], 344 configurable: !!mutator['[[Configurable]]'] 345 }); 346 var data = dataDescriptor(); 347 t.deepEqual(ES.FromPropertyDescriptor(data), { 348 value: data['[[Value]]'], 349 writable: data['[[Writable]]'], 350 enumerable: !!data['[[Enumerable]]'], 351 configurable: !!data['[[Configurable]]'] 352 }); 353 354 t['throws']( 355 function () { ES.FromPropertyDescriptor(genericDescriptor()); }, 356 TypeError, 357 'a complete Property Descriptor is required' 358 ); 359 360 t.end(); 361}); 362 363test('ToPropertyDescriptor', function (t) { 364 forEach(nonNullPrimitives.concat(null), function (primitive) { 365 t['throws']( 366 function () { ES.ToPropertyDescriptor(primitive); }, 367 TypeError, 368 primitive + ' is not an Object' 369 ); 370 }); 371 372 var accessor = accessorDescriptor(); 373 t.deepEqual(ES.ToPropertyDescriptor({ 374 get: accessor['[[Get]]'], 375 enumerable: !!accessor['[[Enumerable]]'], 376 configurable: !!accessor['[[Configurable]]'] 377 }), accessor); 378 379 var mutator = mutatorDescriptor(); 380 t.deepEqual(ES.ToPropertyDescriptor({ 381 set: mutator['[[Set]]'], 382 enumerable: !!mutator['[[Enumerable]]'], 383 configurable: !!mutator['[[Configurable]]'] 384 }), mutator); 385 386 var data = dataDescriptor(); 387 t.deepEqual(ES.ToPropertyDescriptor({ 388 value: data['[[Value]]'], 389 writable: data['[[Writable]]'], 390 configurable: !!data['[[Configurable]]'] 391 }), data); 392 393 var both = bothDescriptor(); 394 t['throws']( 395 function () { 396 ES.ToPropertyDescriptor({ get: both['[[Get]]'], value: both['[[Value]]'] }); 397 }, 398 TypeError, 399 'data and accessor descriptors are mutually exclusive' 400 ); 401 402 t['throws']( 403 function () { ES.ToPropertyDescriptor({ get: 'not callable' }); }, 404 TypeError, 405 '"get" must be undefined or callable' 406 ); 407 408 t['throws']( 409 function () { ES.ToPropertyDescriptor({ set: 'not callable' }); }, 410 TypeError, 411 '"set" must be undefined or callable' 412 ); 413 414 t.end(); 415}); 416