UNPKG

21.6 kBJavaScriptView Raw
1// Big thanks to V8 folks for test ideas.
2// v8/test/mjsunit/harmony/collections.js
3
4var Assertion = expect().constructor;
5Assertion.addMethod('theSameSet', function (otherArray) {
6 var array = this._obj;
7
8 expect(Array.isArray(array)).to.equal(true);
9 expect(Array.isArray(otherArray)).to.equal(true);
10
11 var diff = array.filter(function (value) {
12 return otherArray.every(function (otherValue) {
13 var areBothNaN = typeof value === 'number' && typeof otherValue === 'number' && value !== value && otherValue !== otherValue;
14 return !areBothNaN && value !== otherValue;
15 });
16 });
17
18 this.assert(
19 diff.length === 0,
20 'expected #{this} to be equal to #{exp} (as sets, i.e. no order)',
21 array,
22 otherArray
23 );
24});
25
26var $iterator$ = typeof Symbol === 'function' ? Symbol.iterator : '_es6-shim iterator_';
27if (typeof Set === 'function' && typeof Set.prototype['@@iterator'] === 'function') {
28 $iterator$ = '@@iterator';
29}
30
31Assertion.addMethod('iterations', function (expected) {
32 var iterator = this._obj[$iterator$]();
33
34 expect(Array.isArray(expected)).to.equal(true);
35 var expectedValues = expected.slice();
36
37 var result;
38 do {
39 result = iterator.next();
40 expect(result.value).to.eql(expectedValues.shift());
41 } while (!result.done);
42});
43
44describe('Set', function () {
45 var functionsHaveNames = (function foo() {}).name === 'foo';
46 var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
47 var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
48 var ifGetPrototypeOfIt = Object.getPrototypeOf ? it : xit;
49
50 var range = function (from, to) {
51 var result = [];
52 for (var value = from; value < to; value++) {
53 result.push(value);
54 }
55 return result;
56 };
57
58 var prototypePropIsEnumerable = Object.prototype.propertyIsEnumerable.call(function () {}, 'prototype');
59 var expectNotEnumerable = function (object) {
60 if (prototypePropIsEnumerable && typeof object === 'function') {
61 expect(Object.keys(object)).to.eql(['prototype']);
62 } else {
63 expect(Object.keys(object)).to.eql([]);
64 }
65 };
66
67 var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
68 var isSymbol = function (sym) {
69 return typeof Sym === 'function' && typeof sym === 'symbol';
70 };
71 var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : xit;
72
73 var testSet = function (set, key) {
74 expect(set.has(key)).to.equal(false);
75 expect(set['delete'](key)).to.equal(false);
76 expect(set.add(key)).to.equal(set);
77 expect(set.has(key)).to.equal(true);
78 expect(set['delete'](key)).to.equal(true);
79 expect(set.has(key)).to.equal(false);
80 expect(set.add(key)).to.equal(set); // add it back
81 };
82
83 if (typeof Set === 'undefined') {
84 return it('exists', function () {
85 expect(typeof Set).to.equal('function');
86 });
87 }
88
89 var set;
90 beforeEach(function () {
91 set = new Set();
92 });
93
94 afterEach(function () {
95 set = null;
96 });
97
98 it('set iteration', function () {
99 expect(set.add('a')).to.equal(set);
100 expect(set.add('b')).to.equal(set);
101 expect(set.add('c')).to.equal(set);
102 expect(set.add('d')).to.equal(set);
103
104 var keys = [];
105 var iterator = set.keys();
106 keys.push(iterator.next().value);
107 expect(set['delete']('a')).to.equal(true);
108 expect(set['delete']('b')).to.equal(true);
109 expect(set['delete']('c')).to.equal(true);
110 expect(set.add('e')).to.equal(set);
111 keys.push(iterator.next().value);
112 keys.push(iterator.next().value);
113
114 expect(iterator.next().done).to.equal(true);
115 expect(set.add('f')).to.equal(set);
116 expect(iterator.next().done).to.equal(true);
117 expect(keys).to.eql(['a', 'd', 'e']);
118 });
119
120 ifShimIt('is on the exported object', function () {
121 var exported = require('../');
122 expect(exported.Set).to.equal(Set);
123 });
124
125 it('should exist in global namespace', function () {
126 expect(typeof Set).to.equal('function');
127 });
128
129 it('has the right arity', function () {
130 expect(Set).to.have.property('length', 0);
131 });
132
133 it('returns the set from #add() for chaining', function () {
134 expect(set.add({})).to.equal(set);
135 });
136
137 it('should return false when deleting an item not in the set', function () {
138 expect(set.has('a')).to.equal(false);
139 expect(set['delete']('a')).to.equal(false);
140 });
141
142 it('should accept an iterable as argument', function () {
143 testSet(set, 'a');
144 testSet(set, 'b');
145 var set2 = new Set(set);
146 expect(set2.has('a')).to.equal(true);
147 expect(set2.has('b')).to.equal(true);
148 expect(set2).to.have.iterations(['a', 'b']);
149 });
150
151 it('accepts an array as an argument', function () {
152 var arr = ['a', 'b', 'c'];
153 var setFromArray = new Set(arr);
154 expect(setFromArray).to.have.iterations(['a', 'b', 'c']);
155 });
156
157 it('should not be callable without "new"', function () {
158 expect(Set).to['throw'](TypeError);
159 });
160
161 it('should be subclassable', function () {
162 if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
163 var MySet = function MySet() {
164 var actualSet = new Set(['a', 'b']);
165 Object.setPrototypeOf(actualSet, MySet.prototype);
166 return actualSet;
167 };
168 Object.setPrototypeOf(MySet, Set);
169 MySet.prototype = Object.create(Set.prototype, {
170 constructor: { value: MySet }
171 });
172
173 var mySet = new MySet();
174 testSet(mySet, 'c');
175 testSet(mySet, 'd');
176 expect(mySet).to.have.iterations(['a', 'b', 'c', 'd']);
177 });
178
179 it('should has valid getter and setter calls', function () {
180 ['add', 'has', 'delete'].forEach(function (method) {
181 expect(function () {
182 set[method]({});
183 }).to.not['throw']();
184 });
185 });
186
187 it('uses SameValueZero even on a Set of size > 4', function () {
188 var firstFour = [1, 2, 3, 4];
189 var fourSet = new Set(firstFour);
190 expect(fourSet.size).to.equal(4);
191 expect(fourSet.has(-0)).to.equal(false);
192 expect(fourSet.has(0)).to.equal(false);
193
194 fourSet.add(-0);
195
196 expect(fourSet.size).to.equal(5);
197 expect(fourSet.has(0)).to.equal(true);
198 expect(fourSet.has(-0)).to.equal(true);
199 });
200
201 it('should work as expected', function () {
202 // Run this test twice, one with the "fast" implementation (which only
203 // allows string and numeric keys) and once with the "slow" impl.
204 [true, false].forEach(function (slowkeys) {
205 set = new Set();
206
207 range(1, 20).forEach(function (number) {
208 if (slowkeys) { testSet(set, {}); }
209 testSet(set, number);
210 testSet(set, number / 100);
211 testSet(set, 'key-' + number);
212 testSet(set, String(number));
213 if (slowkeys) { testSet(set, Object(String(number))); }
214 });
215
216 var testkeys = [+0, Infinity, -Infinity, NaN];
217 if (slowkeys) {
218 testkeys.push(true, false, null, undefined);
219 }
220 testkeys.forEach(function (number) {
221 testSet(set, number);
222 testSet(set, String(number));
223 });
224 testSet(set, '');
225
226 // -0 and +0 should be the same key (Set uses SameValueZero)
227 expect(set.has(-0)).to.equal(true);
228 expect(set['delete'](+0)).to.equal(true);
229 testSet(set, -0);
230 expect(set.has(+0)).to.equal(true);
231
232 // verify that properties of Object don't peek through.
233 [
234 'hasOwnProperty',
235 'constructor',
236 'toString',
237 'isPrototypeOf',
238 '__proto__',
239 '__parent__',
240 '__count__'
241 ].forEach(function (prop) { testSet(set, prop); });
242 });
243 });
244
245 describe('#size', function () {
246 it('returns the expected size', function () {
247 expect(set.add(1)).to.equal(set);
248 expect(set.add(5)).to.equal(set);
249 expect(set.size).to.equal(2);
250 });
251 });
252
253 describe('#clear()', function () {
254 ifFunctionsHaveNamesIt('has the right name', function () {
255 expect(Set.prototype.clear).to.have.property('name', 'clear');
256 });
257
258 it('is not enumerable', function () {
259 expect(Set.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
260 });
261
262 it('has the right arity', function () {
263 expect(Set.prototype.clear).to.have.property('length', 0);
264 });
265
266 it('clears a Set with only primitives', function () {
267 expect(set.add(1)).to.equal(set);
268 expect(set.size).to.equal(1);
269 expect(set.add(5)).to.equal(set);
270 expect(set.size).to.equal(2);
271 expect(set.has(5)).to.equal(true);
272 set.clear();
273 expect(set.size).to.equal(0);
274 expect(set.has(5)).to.equal(false);
275 });
276
277 it('clears a Set with primitives and objects', function () {
278 expect(set.add(1)).to.equal(set);
279 expect(set.size).to.equal(1);
280 var obj = {};
281 expect(set.add(obj)).to.equal(set);
282 expect(set.size).to.equal(2);
283 expect(set.has(obj)).to.equal(true);
284 set.clear();
285 expect(set.size).to.equal(0);
286 expect(set.has(obj)).to.equal(false);
287 });
288 });
289
290 describe('#keys()', function () {
291 if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'keys')) {
292 return it('exists', function () {
293 expect(Set.prototype).to.have.property('keys');
294 });
295 }
296
297 it('is the same object as #values()', function () {
298 expect(Set.prototype.keys).to.equal(Set.prototype.values);
299 });
300
301 ifFunctionsHaveNamesIt('has the right name', function () {
302 expect(Set.prototype.keys).to.have.property('name', 'values');
303 });
304
305 it('is not enumerable', function () {
306 expect(Set.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
307 });
308
309 it('has the right arity', function () {
310 expect(Set.prototype.keys).to.have.property('length', 0);
311 });
312 });
313
314 describe('#values()', function () {
315 if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'values')) {
316 return it('exists', function () {
317 expect(Set.prototype).to.have.property('values');
318 });
319 }
320
321 ifFunctionsHaveNamesIt('has the right name', function () {
322 expect(Set.prototype.values).to.have.property('name', 'values');
323 });
324
325 it('is not enumerable', function () {
326 expect(Set.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
327 });
328
329 it('has the right arity', function () {
330 expect(Set.prototype.values).to.have.property('length', 0);
331 });
332
333 it('throws when called on a non-Set', function () {
334 var expectedMessage = /^(Method )?Set.prototype.values called on incompatible receiver |^values method called on incompatible |^Cannot create a Set value iterator for a non-Set object.$|^Set.prototype.values: 'this' is not a Set object$|^std_Set_iterator method called on incompatible \w+$|Set.prototype.values requires that \|this\| be Set| is not an object|Set operation called on non-Set object/;
335 var nonSets = [true, false, 'abc', NaN, new Map([[1, 2]]), { a: true }, [1], Object('abc'), Object(NaN)];
336 nonSets.forEach(function (nonSet) {
337 expect(function () { return Set.prototype.values.call(nonSet); }).to['throw'](TypeError, expectedMessage);
338 });
339 });
340 });
341
342 describe('#entries()', function () {
343 if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'entries')) {
344 return it('exists', function () {
345 expect(Set.prototype).to.have.property('entries');
346 });
347 }
348
349 ifFunctionsHaveNamesIt('has the right name', function () {
350 expect(Set.prototype.entries).to.have.property('name', 'entries');
351 });
352
353 it('is not enumerable', function () {
354 expect(Set.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
355 });
356
357 it('has the right arity', function () {
358 expect(Set.prototype.entries).to.have.property('length', 0);
359 });
360 });
361
362 describe('#has()', function () {
363 if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'has')) {
364 return it('exists', function () {
365 expect(Set.prototype).to.have.property('has');
366 });
367 }
368
369 ifFunctionsHaveNamesIt('has the right name', function () {
370 expect(Set.prototype.has).to.have.property('name', 'has');
371 });
372
373 it('is not enumerable', function () {
374 expect(Set.prototype).ownPropertyDescriptor('has').to.have.property('enumerable', false);
375 });
376
377 it('has the right arity', function () {
378 expect(Set.prototype.has).to.have.property('length', 1);
379 });
380 });
381
382 it('should allow NaN values as keys', function () {
383 expect(set.has(NaN)).to.equal(false);
384 expect(set.has(NaN + 1)).to.equal(false);
385 expect(set.has(23)).to.equal(false);
386 expect(set.add(NaN)).to.equal(set);
387 expect(set.has(NaN)).to.equal(true);
388 expect(set.has(NaN + 1)).to.equal(true);
389 expect(set.has(23)).to.equal(false);
390 });
391
392 it('should not have [[Enumerable]] props', function () {
393 expectNotEnumerable(Set);
394 expectNotEnumerable(Set.prototype);
395 expectNotEnumerable(new Set());
396 });
397
398 it('should not have an own constructor', function () {
399 var s = new Set();
400 expect(s).not.to.haveOwnPropertyDescriptor('constructor');
401 expect(s.constructor).to.equal(Set);
402 });
403
404 it('should allow common ecmascript idioms', function () {
405 expect(set instanceof Set).to.equal(true);
406 expect(typeof Set.prototype.add).to.equal('function');
407 expect(typeof Set.prototype.has).to.equal('function');
408 expect(typeof Set.prototype['delete']).to.equal('function');
409 });
410
411 it('should have a unique constructor', function () {
412 expect(Set.prototype).to.not.equal(Object.prototype);
413 });
414
415 describe('has an iterator that works with Array.from', function () {
416 if (!Object.prototype.hasOwnProperty.call(Array, 'from')) {
417 return it('requires Array.from to exist', function () {
418 expect(Array).to.have.property('from');
419 });
420 }
421
422 var values = [1, NaN, false, true, null, undefined, 'a'];
423
424 it('works with the full set', function () {
425 expect(new Set(values)).to.have.iterations(values);
426 });
427
428 it('works with Set#keys()', function () {
429 expect(new Set(values).keys()).to.have.iterations(values);
430 });
431
432 it('works with Set#values()', function () {
433 expect(new Set(values).values()).to.have.iterations(values);
434 });
435
436 it('works with Set#entries()', function () {
437 expect(new Set(values).entries()).to.have.iterations([
438 [1, 1],
439 [NaN, NaN],
440 [false, false],
441 [true, true],
442 [null, null],
443 [undefined, undefined],
444 ['a', 'a']
445 ]);
446 });
447 });
448
449 ifSymbolIteratorIt('has the right default iteration function', function () {
450 // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
451 expect(Set.prototype).to.have.property(Sym.iterator, Set.prototype.values);
452 });
453
454 it('should preserve insertion order', function () {
455 var arr1 = ['d', 'a', 'b'];
456 var arr2 = [3, 2, 'z', 'a', 1];
457 var arr3 = [3, 2, 'z', {}, 'a', 1];
458
459 [arr1, arr2, arr3].forEach(function (array) {
460 expect(new Set(array)).to.have.iterations(array);
461 });
462 });
463
464 describe('#forEach', function () {
465 var setToIterate;
466 beforeEach(function () {
467 setToIterate = new Set();
468 expect(setToIterate.add('a')).to.equal(setToIterate);
469 expect(setToIterate.add('b')).to.equal(setToIterate);
470 expect(setToIterate.add('c')).to.equal(setToIterate);
471 });
472
473 afterEach(function () {
474 setToIterate = null;
475 });
476
477 ifFunctionsHaveNamesIt('has the right name', function () {
478 expect(Set.prototype.forEach).to.have.property('name', 'forEach');
479 });
480
481 it('is not enumerable', function () {
482 expect(Set.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
483 });
484
485 it('has the right arity', function () {
486 expect(Set.prototype.forEach).to.have.property('length', 1);
487 });
488
489 it('should be iterable via forEach', function () {
490 var expectedSet = ['a', 'b', 'c'];
491 var foundSet = [];
492 setToIterate.forEach(function (value, alsoValue, entireSet) {
493 expect(entireSet).to.equal(setToIterate);
494 expect(value).to.equal(alsoValue);
495 foundSet.push(value);
496 });
497 expect(foundSet).to.eql(expectedSet);
498 });
499
500 it('should iterate over empty keys', function () {
501 var setWithEmptyKeys = new Set();
502 var expectedKeys = [{}, null, undefined, '', NaN, 0];
503 expectedKeys.forEach(function (key) {
504 expect(setWithEmptyKeys.add(key)).to.equal(setWithEmptyKeys);
505 });
506 var foundKeys = [];
507 setWithEmptyKeys.forEach(function (value, key, entireSet) {
508 expect([key]).to.be.theSameSet([value]); // handles NaN correctly
509 expect(entireSet.has(key)).to.equal(true);
510 foundKeys.push(key);
511 });
512 expect(foundKeys).to.be.theSameSet(expectedKeys);
513 });
514
515 it('should support the thisArg', function () {
516 var context = function () {};
517 setToIterate.forEach(function () {
518 expect(this).to.equal(context);
519 }, context);
520 });
521
522 it('should have a length of 1', function () {
523 expect(Set.prototype.forEach.length).to.equal(1);
524 });
525
526 it('should not revisit modified keys', function () {
527 var hasModifiedA = false;
528 setToIterate.forEach(function (value, key) {
529 if (!hasModifiedA && key === 'a') {
530 expect(setToIterate.add('a')).to.equal(setToIterate);
531 hasModifiedA = true;
532 } else {
533 expect(key).not.to.equal('a');
534 }
535 });
536 });
537
538 it('visits keys added in the iterator', function () {
539 var hasAdded = false;
540 var hasFoundD = false;
541 setToIterate.forEach(function (value, key) {
542 if (!hasAdded) {
543 expect(setToIterate.add('d')).to.equal(setToIterate);
544 hasAdded = true;
545 } else if (key === 'd') {
546 hasFoundD = true;
547 }
548 });
549 expect(hasFoundD).to.equal(true);
550 });
551
552 it('visits keys added in the iterator when there is a deletion (slow path)', function () {
553 var hasSeenFour = false;
554 var setToMutate = new Set();
555 expect(setToMutate.add({})).to.equal(setToMutate); // force use of the slow O(N) implementation
556 expect(setToMutate.add('0')).to.equal(setToMutate);
557 setToMutate.forEach(function (value, key) {
558 if (key === '0') {
559 expect(setToMutate['delete']('0')).to.equal(true);
560 expect(setToMutate.add('4')).to.equal(setToMutate);
561 } else if (key === '4') {
562 hasSeenFour = true;
563 }
564 });
565 expect(hasSeenFour).to.equal(true);
566 });
567
568 it('visits keys added in the iterator when there is a deletion (fast path)', function () {
569 var hasSeenFour = false;
570 var setToMutate = new Set();
571 expect(setToMutate.add('0')).to.equal(setToMutate);
572 setToMutate.forEach(function (value, key) {
573 if (key === '0') {
574 expect(setToMutate['delete']('0')).to.equal(true);
575 expect(setToMutate.add('4')).to.equal(setToMutate);
576 } else if (key === '4') {
577 hasSeenFour = true;
578 }
579 });
580 expect(hasSeenFour).to.equal(true);
581 });
582
583 it('does not visit keys deleted before a visit', function () {
584 var hasVisitedC = false;
585 var hasDeletedC = false;
586 setToIterate.forEach(function (value, key) {
587 if (key === 'c') {
588 hasVisitedC = true;
589 }
590 if (!hasVisitedC && !hasDeletedC) {
591 hasDeletedC = setToIterate['delete']('c');
592 expect(hasDeletedC).to.equal(true);
593 }
594 });
595 expect(hasVisitedC).to.equal(false);
596 });
597
598 it('should work after deletion of the current key', function () {
599 var expectedSet = {
600 a: 'a',
601 b: 'b',
602 c: 'c'
603 };
604 var foundSet = {};
605 setToIterate.forEach(function (value, key) {
606 foundSet[key] = value;
607 expect(setToIterate['delete'](key)).to.equal(true);
608 });
609 expect(foundSet).to.eql(expectedSet);
610 });
611
612 it('should convert key -0 to +0', function () {
613 var zeroSet = new Set();
614 var result = [];
615 expect(zeroSet.add(-0)).to.equal(zeroSet);
616 zeroSet.forEach(function (key) {
617 result.push(String(1 / key));
618 });
619 expect(zeroSet.add(1)).to.equal(zeroSet);
620 expect(zeroSet.add(0)).to.equal(zeroSet); // shouldn't cause reordering
621 zeroSet.forEach(function (key) {
622 result.push(String(1 / key));
623 });
624 expect(result.join(', ')).to.equal('Infinity, Infinity, 1');
625 });
626 });
627
628 it('Set.prototype.size should throw TypeError', function () {
629 // see https://github.com/paulmillr/es6-shim/issues/176
630 expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
631 expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
632 });
633
634 it.skip('should throw proper errors when user invokes methods with wrong types of receiver', function () {
635 });
636
637 ifGetPrototypeOfIt('SetIterator identification test prototype inequality', function () {
638 var mapEntriesProto = Object.getPrototypeOf(new Map().entries());
639 var setEntriesProto = Object.getPrototypeOf(new Set().entries());
640 expect(mapEntriesProto).to.not.equal(setEntriesProto);
641 });
642
643 it('SetIterator identification', function () {
644 var fnSetValues = Set.prototype.values;
645 var setSentinel = new Set(['SetSentinel']);
646 var testSet1 = new Set();
647 var testSetValues = testSet1.values();
648 expect(testSetValues.next.call(fnSetValues.call(setSentinel)).value).to.equal('SetSentinel');
649
650 var testMap = new Map();
651 var testMapValues = testMap.values();
652 expect(function () {
653 return testMapValues.next.call(fnSetValues.call(setSentinel)).value;
654 }).to['throw'](TypeError);
655 });
656});