UNPKG

39 kBJavaScriptView Raw
1var runArrayTests = function (it) {
2 'use strict';
3
4 var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
5 var isSymbol = function (sym) {
6 return typeof Sym === 'function' && typeof sym === 'symbol';
7 };
8 var functionsHaveNames = (function foo() {}).name === 'foo';
9 var ifFunctionsHaveNamesIt = functionsHaveNames ? it : it.skip;
10 var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : it.skip;
11 var ifSymbolIteratorAndArrayValuesIt = isSymbol(Sym.iterator) && Array.prototype.values ? it : it.skip;
12 var ifSymbolUnscopablesIt = isSymbol(Sym.unscopables) ? it : it.skip;
13 var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
14 var ifSupportsDescriptorsIt = Object.getOwnPropertyDescriptor ? it : it.skip;
15 var ifHasDunderProtoIt = [].__proto__ === Array.prototype ? it : it.skip; // eslint-disable-line no-proto
16
17 var isNegativeZero = function (x) {
18 return (1 / x) < 0;
19 };
20
21 describe('Array', function () {
22 var list = [5, 10, 15, 20];
23
24 ifShimIt('is on the exported object', function () {
25 var exported = require('../');
26 expect(exported.Array).to.equal(Array);
27 });
28
29 describe('@@iterator', function () {
30 ifSymbolIteratorIt('uses Symbol.iterator if available', function () {
31 var b = {};
32 var c = {};
33 var a = [b, c];
34 var iteratorFn = a[Sym.iterator];
35 var iterator = iteratorFn.call(a);
36 expect(iterator.next()).to.eql({ done: false, value: b });
37 expect(iterator.next()).to.eql({ done: false, value: c });
38 expect(iterator.next()).to.eql({ done: true, value: undefined });
39 });
40
41 ifSymbolIteratorAndArrayValuesIt('has the right default iteration function', function () {
42 // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
43 expect(Array.prototype).to.have.property(Sym.iterator, Array.prototype.values);
44 });
45 });
46
47 describe('.from()', function () {
48 if (!Object.prototype.hasOwnProperty.call(Array, 'from')) {
49 return it('exists', function () {
50 expect(Array).to.have.property('from');
51 });
52 }
53
54 ifFunctionsHaveNamesIt('has the correct name', function () {
55 expect(Array.from).to.have.property('name', 'from');
56 });
57
58 it('has the right arity', function () {
59 expect(Array.from).to.have.property('length', 1);
60 });
61
62 it('is not enumerable', function () {
63 expect(Array).ownPropertyDescriptor('from').to.have.property('enumerable', false);
64 });
65
66 it('works with primitives', function () {
67 expect(Array.from(false)).to.eql([]);
68 expect(Array.from(true)).to.eql([]);
69 expect(Array.from(-Infinity)).to.eql([]);
70 expect(Array.from(-0)).to.eql([]);
71 expect(Array.from(0)).to.eql([]);
72 expect(Array.from(1)).to.eql([]);
73 expect(Array.from(Infinity)).to.eql([]);
74 });
75
76 it('should create correct array from iterable', function () {
77 (function () {
78 expect(Array.from(arguments)).to.eql([0, 1, 2]);
79 }(0, 1, 2));
80
81 expect(Array.from([null, undefined, 0.1248, -0, 0])).to.eql([null, undefined, 0.1248, -0, 0]);
82
83 if (Array.prototype.values) {
84 expect(Array.from([null, undefined, 0.1248, -0, 0].values())).to.eql([null, undefined, 0.1248, -0, 0]);
85 }
86 });
87
88 it('works with arraylike objects', function () {
89 expect(Array.from({ length: 1 })).to.eql([undefined]);
90 expect(Array.from({ 0: 'a', 1: 'b', length: 2 })).to.eql(['a', 'b']);
91 });
92
93 it('swallows negative lengths', function () {
94 expect(Array.from({ length: -1 })).to.have.property('length', 0);
95 expect(Array.from({ length: -Infinity })).to.have.property('length', 0);
96 expect(Array.from({ length: -0 })).to.have.property('length', 0);
97 expect(Array.from({ length: -42 })).to.have.property('length', 0);
98 });
99
100 it('works with strings', function () {
101 expect(Array.from('')).to.eql([]);
102 expect(Array.from('abc')).to.eql('abc'.split(''));
103 });
104
105 it('should handle empty iterables correctly', function () {
106 (function () {
107 expect(Array.from(arguments)).to.eql([]);
108 }());
109 expect(Array.from([])).to.eql([]);
110 expect(Array.from({})).to.eql([]);
111 expect(Array.from({ a: 1 })).to.eql([]);
112 });
113
114 it('should work with other constructors', function () {
115 var Foo = function FooBar(length, args) {
116 this.args = args;
117 this.length = length;
118 };
119 var args = ['a', 'b', 'c'];
120 var expected = new Foo(args.length);
121 args.forEach(function (arg, index) {
122 expected[index] = arg;
123 });
124 expect(Array.from.call(Foo, args)).to.be.an.instanceOf(Foo);
125 expect(Array.from.call(Foo, args)).to.eql(expected);
126 });
127
128 describe('map functions', function () {
129 it('supports a map function', function () {
130 var original = [1, 2, 3];
131 var mapper = function (item) {
132 return item * 2;
133 };
134 var mapped = Array.from(original, mapper);
135 expect(mapped).to.eql([2, 4, 6]);
136 });
137
138 it('passes both (and only) the item and the current index to the map function', function () {
139 var original = [1, 2, 3];
140 var expectedItems = [1, 2, 3];
141 var expectedIndices = [0, 1, 2];
142
143 var actualItems = [];
144 var actualIndices = [];
145 var mapper = function (item, index) {
146 actualItems.push(item);
147 actualIndices.push(index);
148 expect(arguments).to.have.property('length', 2);
149 return item;
150 };
151
152 var mapped = Array.from(original, mapper);
153 expect(mapped).to.eql(expectedItems);
154 expect(actualItems).to.eql(expectedItems);
155 expect(actualIndices).to.eql(expectedIndices);
156 });
157
158 it('passes both the item and the current index to the map function with a "this" value', function () {
159 var original = [1, 2, 3];
160 var expectedItems = [1, 2, 3];
161 var expectedIndices = [0, 1, 2];
162 var expectedContext = {};
163
164 var actualItems = [];
165 var actualIndices = [];
166 var mapper = function (item, index) {
167 actualItems.push(item);
168 actualIndices.push(index);
169 expect(arguments).to.have.property('length', 2);
170 expect(this).to.eql(expectedContext);
171 return item;
172 };
173
174 var mapped = Array.from(original, mapper, expectedContext);
175 expect(mapped).to.eql(expectedItems);
176 expect(actualItems).to.eql(expectedItems);
177 expect(actualIndices).to.eql(expectedIndices);
178 });
179
180 it('accepts an object thisArg', function () {
181 var context = {};
182 Array.from([1, 2, 3], function () {
183 expect(this).to.equal(context);
184 }, context);
185 });
186
187 it('accepts a primitive thisArg', function () {
188 Array.from([1, 2, 3], function () {
189 expect(this.valueOf()).to.equal(42);
190 expect(Object.prototype.toString.call(this)).to.equal('[object Number]');
191 }, 42);
192 });
193
194 it('accepts a falsy thisArg', function () {
195 Array.from([1, 2, 3], function () {
196 expect(this.valueOf()).to.equal(false);
197 expect(Object.prototype.toString.call(this)).to.equal('[object Boolean]');
198 }, false);
199 });
200 });
201
202 it('does not throw when provided an undefined second arg', function () {
203 expect(Array.from([], undefined)).to.eql([]);
204 });
205
206 it('throws when provided a nonfunction second arg', function () {
207 expect(function () { Array.from([], null); }).to['throw'](TypeError);
208 expect(function () { Array.from([], false); }).to['throw'](TypeError);
209 expect(function () { Array.from([], true); }).to['throw'](TypeError);
210 expect(function () { Array.from([], /a/g); }).to['throw'](TypeError);
211 expect(function () { Array.from([], {}); }).to['throw'](TypeError);
212 expect(function () { Array.from([], []); }).to['throw'](TypeError);
213 expect(function () { Array.from([], ''); }).to['throw'](TypeError);
214 expect(function () { Array.from([], 3); }).to['throw'](TypeError);
215 });
216
217 it('supports a this arg', function () {
218 var original = [1, 2, 3];
219 var context = {};
220 var mapper = function (item) {
221 expect(this).to.equal(context);
222 return item * 2;
223 };
224 var mapped = Array.from(original, mapper, context);
225 expect(mapped).to.eql([2, 4, 6]);
226 });
227
228 it('throws when provided null or undefined', function () {
229 expect(function () { Array.from(); }).to['throw'](TypeError);
230 expect(function () { Array.from(undefined); }).to['throw'](TypeError);
231 expect(function () { Array.from(null); }).to['throw'](TypeError);
232 });
233
234 it('removes holes', function () {
235 /* eslint-disable no-sparse-arrays */
236 var input = [0, , 2];
237 var result = Array.from([0, , 2]);
238 /* eslint-enable no-sparse-arrays */
239 expect(1 in input).to.equal(false);
240 expect(1 in result).to.equal(true);
241 expect(result).to.eql([0, undefined, 2]);
242 });
243
244 it('works with this flaky example', function () {
245 expect(Array.from([1, NaN, false])).to.eql([1, NaN, false]);
246 });
247
248 ifSupportsDescriptorsIt('works when Object.prototype has a throwing setter', function () {
249 // TODO: breaks in Chrome 17, IE 9, Safari 5.1-6
250 var key = 10;
251 /* eslint no-extend-native: 0 */
252 Object.defineProperty(Object.prototype, key, {
253 configurable: true,
254 get: function () {}, // eslint-disable-line getter-return
255 set: function (v) { throw new EvalError('boom'); }
256 });
257 expect(function () {
258 var arr = [];
259 arr[key] = 42;
260 }).to['throw'](EvalError); // assert thrower
261
262 expect(function () { Array.from({ length: key + 1 }); }).not.to['throw']();
263
264 delete Object.prototype[key];
265 expect(key in Object.prototype).to.equal(false); // assert cleanup
266 });
267 });
268
269 describe('.of()', function () {
270 if (!Object.prototype.hasOwnProperty.call(Array, 'of')) {
271 return it('exists', function () {
272 expect(Array).to.have.property('of');
273 });
274 }
275
276 ifFunctionsHaveNamesIt('has the correct name', function () {
277 expect(Array.of).to.have.property('name', 'of');
278 });
279
280 it('has the right arity', function () {
281 expect(Array.of).to.have.property('length', 0);
282 });
283
284 it('is not enumerable', function () {
285 expect(Array).ownPropertyDescriptor('of').to.have.property('enumerable', false);
286 });
287
288 it('should create correct array from arguments', function () {
289 expect(Array.of(1, null, undefined)).to.eql([1, null, undefined]);
290 });
291
292 it('should work with other constructors', function () {
293 var Foo = function FooBar(length) {
294 this.args = Array.prototype.slice.call(arguments, 1);
295 this.length = length;
296 };
297 var args = ['a', 'b', 'c'];
298 var expected = new Foo(args.length);
299 args.forEach(function (arg, index) {
300 expected[index] = arg;
301 });
302 expect(Array.of.apply(Foo, args)).to.be.an.instanceOf(Foo);
303 expect(Array.of.apply(Foo, args)).to.eql(expected);
304 });
305
306 describe('without Array.from', function () {
307 var originalFrom = Array.from;
308 beforeEach(function () {
309 Array.from = 42;
310 });
311
312 afterEach(function () {
313 Array.from = originalFrom;
314 });
315
316 it('still works', function () {
317 expect(Array.of(1, 2, 3)).to.eql([1, 2, 3]);
318 });
319 });
320 });
321
322 describe('#copyWithin()', function () {
323 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'copyWithin')) {
324 return it('exists', function () {
325 expect(Array.prototype).to.have.property('copyWithin');
326 });
327 }
328
329 ifFunctionsHaveNamesIt('has the correct name', function () {
330 expect(Array.prototype.copyWithin).to.have.property('name', 'copyWithin');
331 });
332
333 it('has the right arity', function () {
334 expect(Array.prototype.copyWithin).to.have.property('length', 2);
335 });
336
337 it('is not enumerable', function () {
338 expect(Array.prototype).ownPropertyDescriptor('copyWithin').to.have.property('enumerable', false);
339 });
340
341 it('modifies the object in-place', function () {
342 var arr = [1, 2, 3, 4, 5];
343 expect(arr.copyWithin(0, 3)).to.eql([4, 5, 3, 4, 5]);
344 expect(arr).to.eql([4, 5, 3, 4, 5]);
345 });
346
347 it('works with 2 args', function () {
348 expect([1, 2, 3, 4, 5].copyWithin(0, 3)).to.eql([4, 5, 3, 4, 5]);
349 expect([1, 2, 3, 4, 5].copyWithin(1, 3)).to.eql([1, 4, 5, 4, 5]);
350 expect([1, 2, 3, 4, 5].copyWithin(1, 2)).to.eql([1, 3, 4, 5, 5]);
351 expect([1, 2, 3, 4, 5].copyWithin(2, 2)).to.eql([1, 2, 3, 4, 5]);
352 });
353
354 it('works with 3 args', function () {
355 expect([1, 2, 3, 4, 5].copyWithin(0, 3, 4)).to.eql([4, 2, 3, 4, 5]);
356 expect([1, 2, 3, 4, 5].copyWithin(1, 3, 4)).to.eql([1, 4, 3, 4, 5]);
357 expect([1, 2, 3, 4, 5].copyWithin(1, 2, 4)).to.eql([1, 3, 4, 4, 5]);
358 });
359
360 it('works with negative args', function () {
361 expect([1, 2, 3, 4, 5].copyWithin(0, -2)).to.eql([4, 5, 3, 4, 5]);
362 expect([1, 2, 3, 4, 5].copyWithin(0, -2, -1)).to.eql([4, 2, 3, 4, 5]);
363 expect([1, 2, 3, 4, 5].copyWithin(-4, -3, -2)).to.eql([1, 3, 3, 4, 5]);
364 expect([1, 2, 3, 4, 5].copyWithin(-4, -3, -1)).to.eql([1, 3, 4, 4, 5]);
365 expect([1, 2, 3, 4, 5].copyWithin(-4, -3)).to.eql([1, 3, 4, 5, 5]);
366 });
367
368 it('works with arraylike objects', function () {
369 var args = (function () { return arguments; }(1, 2, 3));
370 expect(Array.isArray(args)).to.equal(false);
371 var argsClass = Object.prototype.toString.call(args);
372 expect(Array.prototype.slice.call(args)).to.eql([1, 2, 3]);
373 Array.prototype.copyWithin.call(args, -2, 0);
374 expect(Array.prototype.slice.call(args)).to.eql([1, 1, 2]);
375 expect(Object.prototype.toString.call(args)).to.equal(argsClass);
376 });
377
378 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
379 var unscopables = Array.prototype[Sym.unscopables];
380 expect(!!unscopables).to.equal(true);
381 expect(unscopables.copyWithin).to.equal(true);
382 });
383
384 it('should delete the target key if the source key is not present', function () {
385 /* eslint-disable no-sparse-arrays */
386 expect([, 1, 2].copyWithin(1, 0)).to.eql([, , 1]);
387 /* eslint-enable no-sparse-arrays */
388 });
389
390 it('should check inherited properties as well', function () {
391 var Parent = function Parent() {};
392 Parent.prototype[0] = 'foo';
393 var sparse = new Parent();
394 sparse[1] = 1;
395 sparse[2] = 2;
396 sparse.length = 3;
397 var result = Array.prototype.copyWithin.call(sparse, 1, 0);
398 expect(result).to.have.property('0');
399 expect(result).not.to.have.ownProperty('0');
400 expect(result).to.have.ownProperty('1');
401 expect(result).to.eql({ 0: 'foo', 1: 'foo', 2: 1, length: 3 });
402 });
403
404 // https://github.com/tc39/test262/pull/2443
405 describe('security issues', function () {
406 // make a long integer Array
407 var longDenseArray = function longDenseArray() {
408 var a = [0];
409 for (var i = 0; i < 1024; i++) {
410 a[i] = i;
411 }
412 return a;
413 };
414
415 describe('coerced-values-start-change-start', function () {
416 var currArray;
417 var shorten = function shorten() {
418 currArray.length = 20;
419 return 1000;
420 };
421
422 it('coercion side-effect makes start out of bounds', function () {
423 currArray = longDenseArray();
424 var array = [];
425 array.length = 20;
426
427 expect(currArray.copyWithin(0, { valueOf: shorten })).to.deep.equal(array);
428 });
429
430 ifHasDunderProtoIt('coercion side-effect makes start out of bounds with prototype', function () {
431 currArray = longDenseArray();
432 Object.setPrototypeOf(currArray, longDenseArray());
433
434 var array2 = longDenseArray();
435 array2.length = 20;
436 for (var i = 0; i < 24; i++) {
437 array2[i] = Object.getPrototypeOf(currArray)[i + 1000];
438 }
439
440 expect(currArray.copyWithin(0, { valueOf: shorten })).to.have.deep.members(array2);
441 });
442 });
443
444 describe('coerced-values-start-change-target', function () {
445 it('coercion side-effect makes target out of bounds', function () {
446 var shorten = function shorten() {
447 currArray.length = 20;
448 return 1;
449 };
450
451 var array = longDenseArray();
452 array.length = 20;
453 for (var i = 0; i < 19; i++) {
454 array[i + 1000] = array[i + 1];
455 }
456
457 var currArray = longDenseArray();
458
459 expect(currArray.copyWithin(1000, { valueOf: shorten })).to.deep.equal(array);
460 });
461 });
462 });
463 });
464
465 describe('#find()', function () {
466 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'find')) {
467 return it('exists', function () {
468 expect(Array.prototype).to.have.property('find');
469 });
470 }
471
472 ifFunctionsHaveNamesIt('has the correct name', function () {
473 expect(Array.prototype.find).to.have.property('name', 'find');
474 });
475
476 it('should have the right arity', function () {
477 expect(Array.prototype.find).to.have.property('length', 1);
478 });
479
480 it('is not enumerable', function () {
481 expect(Array.prototype).ownPropertyDescriptor('find').to.have.property('enumerable', false);
482 });
483
484 it('should find item by predicate', function () {
485 var result = list.find(function (item) { return item === 15; });
486 expect(result).to.equal(15);
487 });
488
489 it('should return undefined when nothing matched', function () {
490 var result = list.find(function (item) { return item === 'a'; });
491 expect(result).to.equal(undefined);
492 });
493
494 it('should throw TypeError when function was not passed', function () {
495 expect(function () { list.find(); }).to['throw'](TypeError);
496 });
497
498 it('should receive all three parameters', function () {
499 var foundIndex = list.find(function (value, index, arr) {
500 expect(list).to.have.property(index, value);
501 expect(list).to.eql(arr);
502 return false;
503 });
504 expect(foundIndex).to.equal(undefined);
505 });
506
507 it('should work with the context argument', function () {
508 var context = {};
509 [1].find(function () { expect(this).to.equal(context); }, context);
510 });
511
512 it('should work with an array-like object', function () {
513 var obj = { 0: 1, 1: 2, 2: 3, length: 3 };
514 var found = Array.prototype.find.call(obj, function (item) { return item === 2; });
515 expect(found).to.equal(2);
516 });
517
518 it('should work with an array-like object with negative length', function () {
519 var obj = { 0: 1, 1: 2, 2: 3, length: -3 };
520 var found = Array.prototype.find.call(obj, function () {
521 throw new Error('should not reach here');
522 });
523 expect(found).to.equal(undefined);
524 });
525
526 it('should work with a sparse array', function () {
527 /* eslint-disable no-sparse-arrays */
528 var obj = [1, , undefined];
529 /* eslint-enable no-sparse-arrays */
530 expect(1 in obj).to.equal(false);
531 var seen = [];
532 var found = obj.find(function (item, idx) {
533 seen.push([idx, item]);
534 return false;
535 });
536 expect(found).to.equal(undefined);
537 expect(seen).to.eql([[0, 1], [1, undefined], [2, undefined]]);
538 });
539
540 it('should work with a sparse array-like object', function () {
541 var obj = { 0: 1, 2: undefined, length: 3.2 };
542 var seen = [];
543 var found = Array.prototype.find.call(obj, function (item, idx) {
544 seen.push([idx, item]);
545 return false;
546 });
547 expect(found).to.equal(undefined);
548 expect(seen).to.eql([[0, 1], [1, undefined], [2, undefined]]);
549 });
550
551 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
552 var unscopables = Array.prototype[Sym.unscopables];
553 expect(!!unscopables).to.equal(true);
554 expect(unscopables.find).to.equal(true);
555 });
556 });
557
558 describe('#findIndex()', function () {
559 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'findIndex')) {
560 return it('exists', function () {
561 expect(Array.prototype).to.have.property('findIndex');
562 });
563 }
564
565 ifFunctionsHaveNamesIt('has the correct name', function () {
566 expect(Array.prototype.findIndex).to.have.property('name', 'findIndex');
567 });
568
569 it('should have the right arity', function () {
570 expect(Array.prototype.findIndex).to.have.property('length', 1);
571 });
572
573 it('is not enumerable', function () {
574 expect(Array.prototype).ownPropertyDescriptor('findIndex').to.have.property('enumerable', false);
575 });
576
577 it('should find item key by predicate', function () {
578 var result = list.findIndex(function (item) { return item === 15; });
579 expect(result).to.equal(2);
580 });
581
582 it('should return -1 when nothing matched', function () {
583 var result = list.findIndex(function (item) { return item === 'a'; });
584 expect(result).to.equal(-1);
585 });
586
587 it('should throw TypeError when function was not passed', function () {
588 expect(function () { list.findIndex(); }).to['throw'](TypeError);
589 });
590
591 it('should receive all three parameters', function () {
592 var foundIndex = list.findIndex(function (value, index, arr) {
593 expect(list[index]).to.equal(value);
594 expect(list).to.eql(arr);
595 return false;
596 });
597 expect(foundIndex).to.equal(-1);
598 });
599
600 it('should work with the context argument', function () {
601 var context = {};
602 [1].findIndex(function () { expect(this).to.equal(context); }, context);
603 });
604
605 it('should work with an array-like object', function () {
606 var obj = { 0: 1, 1: 2, 2: 3, length: 3 };
607 var foundIndex = Array.prototype.findIndex.call(obj, function (item) { return item === 2; });
608 expect(foundIndex).to.equal(1);
609 });
610
611 it('should work with an array-like object with negative length', function () {
612 var obj = { 0: 1, 1: 2, 2: 3, length: -3 };
613 var foundIndex = Array.prototype.findIndex.call(obj, function () {
614 throw new Error('should not reach here');
615 });
616 expect(foundIndex).to.equal(-1);
617 });
618
619 it('should work with a sparse array', function () {
620 /* eslint-disable no-sparse-arrays */
621 var obj = [1, , undefined];
622 /* eslint-enable no-sparse-arrays */
623 expect(1 in obj).to.equal(false);
624 var seen = [];
625 var foundIndex = obj.findIndex(function (item, idx) {
626 seen.push([idx, item]);
627 return item === undefined && idx === 2;
628 });
629 expect(foundIndex).to.equal(2);
630 expect(seen).to.eql([[0, 1], [1, undefined], [2, undefined]]);
631 });
632
633 it('should work with a sparse array-like object', function () {
634 var obj = { 0: 1, 2: undefined, length: 3.2 };
635 var seen = [];
636 var foundIndex = Array.prototype.findIndex.call(obj, function (item, idx) {
637 seen.push([idx, item]);
638 return false;
639 });
640 expect(foundIndex).to.equal(-1);
641 expect(seen).to.eql([[0, 1], [1, undefined], [2, undefined]]);
642 });
643
644 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
645 var unscopables = Array.prototype[Sym.unscopables];
646 expect(!!unscopables).to.equal(true);
647 expect(unscopables.findIndex).to.equal(true);
648 });
649 });
650
651 describe('ArrayIterator', function () {
652 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'keys')) {
653 return it('can be tested', function () {
654 expect(Array.prototype).to.have.property('keys');
655 });
656 }
657
658 var arrayIterator;
659 beforeEach(function () {
660 arrayIterator = [1, 2, 3].keys();
661 });
662
663 describe('#next()', function () {
664 it('should work when applied to an ArrayIterator', function () {
665 expect(arrayIterator.next.apply(arrayIterator)).to.eql({ value: 0, done: false });
666 expect(arrayIterator.next.apply(arrayIterator)).to.eql({ value: 1, done: false });
667 expect(arrayIterator.next.apply(arrayIterator)).to.eql({ value: 2, done: false });
668 expect(arrayIterator.next.apply(arrayIterator)).to.eql({ value: undefined, done: true });
669 });
670
671 it('throws when not applied to an ArrayIterator', function () {
672 expect(function () { arrayIterator.next.apply({}); }).to['throw'](TypeError);
673 });
674 });
675 });
676
677 describe('#keys()', function () {
678 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'keys')) {
679 return it('exists', function () {
680 expect(Array.prototype).to.have.property('keys');
681 });
682 }
683
684 ifFunctionsHaveNamesIt('has the correct name', function () {
685 expect(Array.prototype.keys).to.have.property('name', 'keys');
686 });
687
688 it('should have the right arity ', function () {
689 expect(Array.prototype.keys.length).to.equal(0);
690 });
691
692 it('is not enumerable', function () {
693 expect(Array.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
694 });
695
696 describe('basic keys iteration', function () {
697 var mylist = [5, 10, 15, 20];
698 var keys;
699 beforeEach(function () {
700 if (!keys) {
701 keys = mylist.keys();
702 }
703 });
704
705 it('should return 0 on first object', function () {
706 expect(keys.next()).to.eql({ value: 0, done: false });
707 });
708
709 it('should return 1 on second object', function () {
710 expect(keys.next()).to.eql({ value: 1, done: false });
711 });
712
713 it('should return 2 on third object', function () {
714 expect(keys.next()).to.eql({ value: 2, done: false });
715 });
716
717 it('should return 3 on fourth object', function () {
718 expect(keys.next()).to.eql({ value: 3, done: false });
719 });
720
721 it('should set done on completing iteration', function () {
722 expect(keys.next()).to.eql({ value: undefined, done: true });
723 });
724
725 it('once done it should stay done', function () {
726 mylist.push(4);
727 expect(keys.next()).to.eql({ value: undefined, done: true });
728 });
729 });
730
731 it('should not skip sparse keys', function () {
732 var sparse = [1];
733 sparse[2] = 3;
734 var keys = sparse.keys();
735 expect(keys.next()).to.eql({ value: 0, done: false });
736 expect(keys.next()).to.eql({ value: 1, done: false });
737 expect(keys.next()).to.eql({ value: 2, done: false });
738 expect(keys.next()).to.eql({ value: undefined, done: true });
739 });
740
741 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
742 var unscopables = Array.prototype[Sym.unscopables];
743 expect(!!unscopables).to.equal(true);
744 expect(unscopables.keys).to.equal(true);
745 });
746 });
747
748 describe('#values()', function () {
749 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'values')) {
750 return it('exists', function () {
751 expect(Array.prototype).to.have.property('values');
752 });
753 }
754
755 ifFunctionsHaveNamesIt('has the correct name', function () {
756 expect(Array.prototype.values).to.have.property('name', 'values');
757 });
758
759 it('has the right arity', function () {
760 expect(Array.prototype.values).to.have.property('length', 0);
761 });
762
763 it('is not enumerable', function () {
764 expect(Array.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
765 });
766
767 describe('basic list iteration', function () {
768 var mylist = [5, 10, 15, 20];
769 var values;
770 beforeEach(function () {
771 if (!values) {
772 values = mylist.values();
773 }
774 });
775
776 it('should return 5 on first object', function () {
777 expect(values.next()).to.eql({ value: 5, done: false });
778 });
779
780 it('should return 10 on second object', function () {
781 expect(values.next()).to.eql({ value: 10, done: false });
782 });
783
784 it('should return 15 on third object', function () {
785 expect(values.next()).to.eql({ value: 15, done: false });
786 });
787
788 it('should return 20 on fourth object', function () {
789 expect(values.next()).to.eql({ value: 20, done: false });
790 });
791
792 it('should set done on completing iteration', function () {
793 expect(values.next()).to.eql({ value: undefined, done: true });
794 });
795
796 it('once done it should stay done', function () {
797 mylist.push(4);
798 expect(values.next()).to.eql({ value: undefined, done: true });
799 });
800 });
801
802 it('should not skip sparse values', function () {
803 var sparse = [1];
804 sparse[2] = 3;
805 var values = sparse.values();
806 expect(values.next()).to.eql({ value: 1, done: false });
807 expect(values.next()).to.eql({ value: undefined, done: false });
808 expect(values.next()).to.eql({ value: 3, done: false });
809 expect(values.next()).to.eql({ value: undefined, done: true });
810 });
811
812 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
813 var unscopables = Array.prototype[Sym.unscopables];
814 expect(!!unscopables).to.equal(true);
815 expect(unscopables.values).to.equal(true);
816 });
817 });
818
819 describe('#entries()', function () {
820 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'entries')) {
821 return it('exists', function () {
822 expect(Array.prototype).to.have.property('entries');
823 });
824 }
825
826 ifFunctionsHaveNamesIt('has the correct name', function () {
827 expect(Array.prototype.entries).to.have.property('name', 'entries');
828 });
829
830 it('has the right arity', function () {
831 expect(Array.prototype.entries).to.have.property('length', 0);
832 });
833
834 it('is not enumerable', function () {
835 expect(Array.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
836 });
837
838 describe('basic list iteration', function () {
839 var mylist = [5, 10, 15, 20];
840 var entries;
841 beforeEach(function () {
842 if (!entries) {
843 entries = mylist.entries();
844 }
845 });
846
847 it('should return [0, 5] on first object', function () {
848 var val = entries.next();
849 expect(val).to.eql({ value: [0, 5], done: false });
850 });
851
852 it('should return [1, 10] on second object', function () {
853 var val = entries.next();
854 expect(val).to.eql({ value: [1, 10], done: false });
855 });
856
857 it('should return [2, 15] on third object', function () {
858 var val = entries.next();
859 expect(val).to.eql({ value: [2, 15], done: false });
860 });
861
862 it('should return [3, 20] on fourth object', function () {
863 var val = entries.next();
864 expect(val).to.eql({ value: [3, 20], done: false });
865 });
866
867 it('should set done on completing iteration', function () {
868 var val = entries.next();
869 expect(val).to.eql({ value: undefined, done: true });
870 });
871
872 it('once done it should stay done', function () {
873 mylist.push(4);
874 var val = entries.next();
875 expect(val).to.eql({ value: undefined, done: true });
876 });
877 });
878
879 it('should not skip sparse entries', function () {
880 var sparse = [1];
881 sparse[2] = 3;
882 var entries = sparse.entries();
883 expect(entries.next()).to.eql({ value: [0, 1], done: false });
884 expect(entries.next()).to.eql({ value: [1, undefined], done: false });
885 expect(entries.next()).to.eql({ value: [2, 3], done: false });
886 expect(entries.next()).to.eql({ value: undefined, done: true });
887 });
888
889 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
890 var unscopables = Array.prototype[Sym.unscopables];
891 expect(!!unscopables).to.equal(true);
892 expect(unscopables.entries).to.equal(true);
893 });
894 });
895
896 describe('#fill()', function () {
897 if (!Object.prototype.hasOwnProperty.call(Array.prototype, 'fill')) {
898 return it('exists', function () {
899 expect(Array.prototype).to.have.property('fill');
900 });
901 }
902
903 ifFunctionsHaveNamesIt('has the correct name', function () {
904 expect(Array.prototype.fill).to.have.property('name', 'fill');
905 });
906
907 it('has the right arity', function () {
908 expect(Array.prototype.fill).to.have.property('length', 1);
909 });
910
911 it('is not enumerable', function () {
912 expect(Array.prototype).ownPropertyDescriptor('fill').to.have.property('enumerable', false);
913 });
914
915 it('works with just a value', function () {
916 var original = [1, 2, 3, 4, 5, 6];
917 var filled = [-1, -1, -1, -1, -1, -1];
918
919 expect(original.fill(-1)).to.eql(filled);
920 });
921
922 it('accepts a positive start index', function () {
923 var original = [1, 2, 3, 4, 5, 6];
924 var filled = [1, 2, 3, -1, -1, -1];
925
926 expect(original.fill(-1, 3)).to.eql(filled);
927 });
928
929 it('accepts a negative start index', function () {
930 var original = [1, 2, 3, 4, 5, 6];
931 var filled = [1, 2, 3, -1, -1, -1];
932
933 expect(original.fill(-1, -3)).to.eql(filled);
934 });
935
936 it('accepts a negative end index', function () {
937 var original = [1, 2, 3];
938 var filled = [4, 2, 3];
939
940 expect(original.fill(4, -3, -2)).to.eql(filled);
941 });
942
943 it('accepts a large start index', function () {
944 var original = [1, 2, 3, 4, 5, 6];
945 var filled = [1, 2, 3, 4, 5, 6];
946
947 expect(original.fill(-1, 9)).to.eql(filled);
948 });
949
950 ifSymbolUnscopablesIt('should be unscopable if Symbols exist', function () {
951 var unscopables = Array.prototype[Sym.unscopables];
952 expect(!!unscopables).to.equal(true);
953 expect(unscopables.fill).to.equal(true);
954 });
955 });
956
957 // this length converts to "1", preventing a crazy crash in older FF
958 var negativeLength = { 0: 1, length: -Math.pow(2, 32) + 1 };
959 var throwRangeError = function (v, i) {
960 if (i === negativeLength.length >>> 0) {
961 return v;
962 }
963 // note: in nonconforming browsers, this will be called
964 // -1 >>> 0 times, which is 4294967295, so the throw matters.
965 throw new RangeError('should not reach here: length of -1 should clamp to length of 0');
966 };
967 var throwRangeErrorReduce = function throwRangeErrorReduce(acc, v, i) {
968 return throwRangeErrorReduce(v, i);
969 };
970
971 describe('#forEach()', function () {
972 it('uses ToLength to clamp negative values to zero', function () {
973 expect(function () {
974 Array.prototype.forEach.call(negativeLength, throwRangeError);
975 }).not.to['throw']();
976 });
977 });
978
979 describe('#map()', function () {
980 it('uses ToLength to clamp negative values to zero', function () {
981 var mapped;
982 expect(function () {
983 mapped = Array.prototype.map.call(negativeLength, throwRangeError);
984 }).not.to['throw']();
985 expect(mapped).to.eql([]);
986 });
987 });
988
989 describe('#filter()', function () {
990 it('uses ToLength to clamp negative values to zero', function () {
991 var filtered;
992 expect(function () {
993 filtered = Array.prototype.filter.call(negativeLength, throwRangeError);
994 }).not.to['throw']();
995 expect(filtered).to.eql([]);
996 });
997 });
998
999 describe('#some()', function () {
1000 it('uses ToLength to clamp negative values to zero', function () {
1001 var result;
1002 expect(function () {
1003 result = Array.prototype.some.call(negativeLength, throwRangeError);
1004 }).not.to['throw']();
1005 expect(result).to.equal([].some(Object));
1006 });
1007 });
1008
1009 describe('#every()', function () {
1010 it('uses ToLength to clamp negative values to zero', function () {
1011 var result;
1012 expect(function () {
1013 result = Array.prototype.every.call(negativeLength, throwRangeError);
1014 }).not.to['throw']();
1015 expect(result).to.equal([].every(Object));
1016 });
1017 });
1018
1019 describe('#reduce()', function () {
1020 it('uses ToLength to clamp negative values to zero', function () {
1021 var accumulator = {};
1022 var reduced;
1023 expect(function () {
1024 reduced = Array.prototype.reduce.call(negativeLength, throwRangeErrorReduce, accumulator);
1025 }).not.to['throw']();
1026 expect(reduced).to.equal(accumulator);
1027 });
1028 });
1029
1030 describe('#reduceRight()', function () {
1031 it('uses ToLength to clamp negative values to zero', function () {
1032 var negativeOneToUint32minusOne = (-1 >>> 0) - 1;
1033 var obj = { length: -1 };
1034 obj[negativeOneToUint32minusOne] = true;
1035 var accumulator = {};
1036 var reduced;
1037 expect(function () {
1038 reduced = Array.prototype.reduceRight.call(obj, throwRangeErrorReduce, accumulator);
1039 }).not.to['throw']();
1040 expect(reduced).to.equal(accumulator);
1041 });
1042 });
1043
1044 describe('#indexOf()', function () {
1045 it('converts second argument from -0 to +0', function () {
1046 expect(isNegativeZero([true].indexOf(true, -0))).to.equal(false);
1047 });
1048 });
1049 });
1050};
1051
1052describe('clean Object.prototype', function () {
1053 'use strict';
1054
1055 runArrayTests.call(this, it);
1056});
1057
1058describe('polluted Object.prototype', function () {
1059 'use strict';
1060
1061 var shimmedIt = function () {
1062 /* eslint-disable no-extend-native */
1063 Object.prototype[1] = 42;
1064 /* eslint-enable no-extend-native */
1065 it.apply(this, arguments);
1066 delete Object.prototype[1];
1067 };
1068 shimmedIt.skip = it.skip;
1069 return runArrayTests.call(this, shimmedIt);
1070});