es5.js 17 KB

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