• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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