123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656 |
- // Big thanks to V8 folks for test ideas.
- // v8/test/mjsunit/harmony/collections.js
- var Assertion = expect().constructor;
- Assertion.addMethod('theSameSet', function (otherArray) {
- var array = this._obj;
- expect(Array.isArray(array)).to.equal(true);
- expect(Array.isArray(otherArray)).to.equal(true);
- var diff = array.filter(function (value) {
- return otherArray.every(function (otherValue) {
- var areBothNaN = typeof value === 'number' && typeof otherValue === 'number' && value !== value && otherValue !== otherValue;
- return !areBothNaN && value !== otherValue;
- });
- });
- this.assert(
- diff.length === 0,
- 'expected #{this} to be equal to #{exp} (as sets, i.e. no order)',
- array,
- otherArray
- );
- });
- var $iterator$ = typeof Symbol === 'function' ? Symbol.iterator : '_es6-shim iterator_';
- if (typeof Set === 'function' && typeof Set.prototype['@@iterator'] === 'function') {
- $iterator$ = '@@iterator';
- }
- Assertion.addMethod('iterations', function (expected) {
- var iterator = this._obj[$iterator$]();
- expect(Array.isArray(expected)).to.equal(true);
- var expectedValues = expected.slice();
- var result;
- do {
- result = iterator.next();
- expect(result.value).to.eql(expectedValues.shift());
- } while (!result.done);
- });
- describe('Set', function () {
- var functionsHaveNames = (function foo() {}).name === 'foo';
- var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
- var ifShimIt = (typeof process !== 'undefined' && process.env.NO_ES6_SHIM) ? it.skip : it;
- var ifGetPrototypeOfIt = Object.getPrototypeOf ? it : xit;
- var range = function (from, to) {
- var result = [];
- for (var value = from; value < to; value++) {
- result.push(value);
- }
- return result;
- };
- var prototypePropIsEnumerable = Object.prototype.propertyIsEnumerable.call(function () {}, 'prototype');
- var expectNotEnumerable = function (object) {
- if (prototypePropIsEnumerable && typeof object === 'function') {
- expect(Object.keys(object)).to.eql(['prototype']);
- } else {
- expect(Object.keys(object)).to.eql([]);
- }
- };
- var Sym = typeof Symbol === 'undefined' ? {} : Symbol;
- var isSymbol = function (sym) {
- return typeof Sym === 'function' && typeof sym === 'symbol';
- };
- var ifSymbolIteratorIt = isSymbol(Sym.iterator) ? it : xit;
- var testSet = function (set, key) {
- expect(set.has(key)).to.equal(false);
- expect(set['delete'](key)).to.equal(false);
- expect(set.add(key)).to.equal(set);
- expect(set.has(key)).to.equal(true);
- expect(set['delete'](key)).to.equal(true);
- expect(set.has(key)).to.equal(false);
- expect(set.add(key)).to.equal(set); // add it back
- };
- if (typeof Set === 'undefined') {
- return it('exists', function () {
- expect(typeof Set).to.equal('function');
- });
- }
- var set;
- beforeEach(function () {
- set = new Set();
- });
- afterEach(function () {
- set = null;
- });
- it('set iteration', function () {
- expect(set.add('a')).to.equal(set);
- expect(set.add('b')).to.equal(set);
- expect(set.add('c')).to.equal(set);
- expect(set.add('d')).to.equal(set);
- var keys = [];
- var iterator = set.keys();
- keys.push(iterator.next().value);
- expect(set['delete']('a')).to.equal(true);
- expect(set['delete']('b')).to.equal(true);
- expect(set['delete']('c')).to.equal(true);
- expect(set.add('e')).to.equal(set);
- keys.push(iterator.next().value);
- keys.push(iterator.next().value);
- expect(iterator.next().done).to.equal(true);
- expect(set.add('f')).to.equal(set);
- expect(iterator.next().done).to.equal(true);
- expect(keys).to.eql(['a', 'd', 'e']);
- });
- ifShimIt('is on the exported object', function () {
- var exported = require('../');
- expect(exported.Set).to.equal(Set);
- });
- it('should exist in global namespace', function () {
- expect(typeof Set).to.equal('function');
- });
- it('has the right arity', function () {
- expect(Set).to.have.property('length', 0);
- });
- it('returns the set from #add() for chaining', function () {
- expect(set.add({})).to.equal(set);
- });
- it('should return false when deleting an item not in the set', function () {
- expect(set.has('a')).to.equal(false);
- expect(set['delete']('a')).to.equal(false);
- });
- it('should accept an iterable as argument', function () {
- testSet(set, 'a');
- testSet(set, 'b');
- var set2 = new Set(set);
- expect(set2.has('a')).to.equal(true);
- expect(set2.has('b')).to.equal(true);
- expect(set2).to.have.iterations(['a', 'b']);
- });
- it('accepts an array as an argument', function () {
- var arr = ['a', 'b', 'c'];
- var setFromArray = new Set(arr);
- expect(setFromArray).to.have.iterations(['a', 'b', 'c']);
- });
- it('should not be callable without "new"', function () {
- expect(Set).to['throw'](TypeError);
- });
- it('should be subclassable', function () {
- if (!Object.setPrototypeOf) { return; } // skip test if on IE < 11
- var MySet = function MySet() {
- var actualSet = new Set(['a', 'b']);
- Object.setPrototypeOf(actualSet, MySet.prototype);
- return actualSet;
- };
- Object.setPrototypeOf(MySet, Set);
- MySet.prototype = Object.create(Set.prototype, {
- constructor: { value: MySet }
- });
- var mySet = new MySet();
- testSet(mySet, 'c');
- testSet(mySet, 'd');
- expect(mySet).to.have.iterations(['a', 'b', 'c', 'd']);
- });
- it('should has valid getter and setter calls', function () {
- ['add', 'has', 'delete'].forEach(function (method) {
- expect(function () {
- set[method]({});
- }).to.not['throw']();
- });
- });
- it('uses SameValueZero even on a Set of size > 4', function () {
- var firstFour = [1, 2, 3, 4];
- var fourSet = new Set(firstFour);
- expect(fourSet.size).to.equal(4);
- expect(fourSet.has(-0)).to.equal(false);
- expect(fourSet.has(0)).to.equal(false);
- fourSet.add(-0);
- expect(fourSet.size).to.equal(5);
- expect(fourSet.has(0)).to.equal(true);
- expect(fourSet.has(-0)).to.equal(true);
- });
- it('should work as expected', function () {
- // Run this test twice, one with the "fast" implementation (which only
- // allows string and numeric keys) and once with the "slow" impl.
- [true, false].forEach(function (slowkeys) {
- set = new Set();
- range(1, 20).forEach(function (number) {
- if (slowkeys) { testSet(set, {}); }
- testSet(set, number);
- testSet(set, number / 100);
- testSet(set, 'key-' + number);
- testSet(set, String(number));
- if (slowkeys) { testSet(set, Object(String(number))); }
- });
- var testkeys = [+0, Infinity, -Infinity, NaN];
- if (slowkeys) {
- testkeys.push(true, false, null, undefined);
- }
- testkeys.forEach(function (number) {
- testSet(set, number);
- testSet(set, String(number));
- });
- testSet(set, '');
- // -0 and +0 should be the same key (Set uses SameValueZero)
- expect(set.has(-0)).to.equal(true);
- expect(set['delete'](+0)).to.equal(true);
- testSet(set, -0);
- expect(set.has(+0)).to.equal(true);
- // verify that properties of Object don't peek through.
- [
- 'hasOwnProperty',
- 'constructor',
- 'toString',
- 'isPrototypeOf',
- '__proto__',
- '__parent__',
- '__count__'
- ].forEach(function (prop) { testSet(set, prop); });
- });
- });
- describe('#size', function () {
- it('returns the expected size', function () {
- expect(set.add(1)).to.equal(set);
- expect(set.add(5)).to.equal(set);
- expect(set.size).to.equal(2);
- });
- });
- describe('#clear()', function () {
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.clear).to.have.property('name', 'clear');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('clear').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.clear).to.have.property('length', 0);
- });
- it('clears a Set with only primitives', function () {
- expect(set.add(1)).to.equal(set);
- expect(set.size).to.equal(1);
- expect(set.add(5)).to.equal(set);
- expect(set.size).to.equal(2);
- expect(set.has(5)).to.equal(true);
- set.clear();
- expect(set.size).to.equal(0);
- expect(set.has(5)).to.equal(false);
- });
- it('clears a Set with primitives and objects', function () {
- expect(set.add(1)).to.equal(set);
- expect(set.size).to.equal(1);
- var obj = {};
- expect(set.add(obj)).to.equal(set);
- expect(set.size).to.equal(2);
- expect(set.has(obj)).to.equal(true);
- set.clear();
- expect(set.size).to.equal(0);
- expect(set.has(obj)).to.equal(false);
- });
- });
- describe('#keys()', function () {
- if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'keys')) {
- return it('exists', function () {
- expect(Set.prototype).to.have.property('keys');
- });
- }
- it('is the same object as #values()', function () {
- expect(Set.prototype.keys).to.equal(Set.prototype.values);
- });
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.keys).to.have.property('name', 'values');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('keys').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.keys).to.have.property('length', 0);
- });
- });
- describe('#values()', function () {
- if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'values')) {
- return it('exists', function () {
- expect(Set.prototype).to.have.property('values');
- });
- }
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.values).to.have.property('name', 'values');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('values').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.values).to.have.property('length', 0);
- });
- it('throws when called on a non-Set', function () {
- 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/;
- var nonSets = [true, false, 'abc', NaN, new Map([[1, 2]]), { a: true }, [1], Object('abc'), Object(NaN)];
- nonSets.forEach(function (nonSet) {
- expect(function () { return Set.prototype.values.call(nonSet); }).to['throw'](TypeError, expectedMessage);
- });
- });
- });
- describe('#entries()', function () {
- if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'entries')) {
- return it('exists', function () {
- expect(Set.prototype).to.have.property('entries');
- });
- }
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.entries).to.have.property('name', 'entries');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('entries').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.entries).to.have.property('length', 0);
- });
- });
- describe('#has()', function () {
- if (!Object.prototype.hasOwnProperty.call(Set.prototype, 'has')) {
- return it('exists', function () {
- expect(Set.prototype).to.have.property('has');
- });
- }
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.has).to.have.property('name', 'has');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('has').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.has).to.have.property('length', 1);
- });
- });
- it('should allow NaN values as keys', function () {
- expect(set.has(NaN)).to.equal(false);
- expect(set.has(NaN + 1)).to.equal(false);
- expect(set.has(23)).to.equal(false);
- expect(set.add(NaN)).to.equal(set);
- expect(set.has(NaN)).to.equal(true);
- expect(set.has(NaN + 1)).to.equal(true);
- expect(set.has(23)).to.equal(false);
- });
- it('should not have [[Enumerable]] props', function () {
- expectNotEnumerable(Set);
- expectNotEnumerable(Set.prototype);
- expectNotEnumerable(new Set());
- });
- it('should not have an own constructor', function () {
- var s = new Set();
- expect(s).not.to.haveOwnPropertyDescriptor('constructor');
- expect(s.constructor).to.equal(Set);
- });
- it('should allow common ecmascript idioms', function () {
- expect(set instanceof Set).to.equal(true);
- expect(typeof Set.prototype.add).to.equal('function');
- expect(typeof Set.prototype.has).to.equal('function');
- expect(typeof Set.prototype['delete']).to.equal('function');
- });
- it('should have a unique constructor', function () {
- expect(Set.prototype).to.not.equal(Object.prototype);
- });
- describe('has an iterator that works with Array.from', function () {
- if (!Object.prototype.hasOwnProperty.call(Array, 'from')) {
- return it('requires Array.from to exist', function () {
- expect(Array).to.have.property('from');
- });
- }
- var values = [1, NaN, false, true, null, undefined, 'a'];
- it('works with the full set', function () {
- expect(new Set(values)).to.have.iterations(values);
- });
- it('works with Set#keys()', function () {
- expect(new Set(values).keys()).to.have.iterations(values);
- });
- it('works with Set#values()', function () {
- expect(new Set(values).values()).to.have.iterations(values);
- });
- it('works with Set#entries()', function () {
- expect(new Set(values).entries()).to.have.iterations([
- [1, 1],
- [NaN, NaN],
- [false, false],
- [true, true],
- [null, null],
- [undefined, undefined],
- ['a', 'a']
- ]);
- });
- });
- ifSymbolIteratorIt('has the right default iteration function', function () {
- // fixed in Webkit https://bugs.webkit.org/show_bug.cgi?id=143838
- expect(Set.prototype).to.have.property(Sym.iterator, Set.prototype.values);
- });
- it('should preserve insertion order', function () {
- var arr1 = ['d', 'a', 'b'];
- var arr2 = [3, 2, 'z', 'a', 1];
- var arr3 = [3, 2, 'z', {}, 'a', 1];
- [arr1, arr2, arr3].forEach(function (array) {
- expect(new Set(array)).to.have.iterations(array);
- });
- });
- describe('#forEach', function () {
- var setToIterate;
- beforeEach(function () {
- setToIterate = new Set();
- expect(setToIterate.add('a')).to.equal(setToIterate);
- expect(setToIterate.add('b')).to.equal(setToIterate);
- expect(setToIterate.add('c')).to.equal(setToIterate);
- });
- afterEach(function () {
- setToIterate = null;
- });
- ifFunctionsHaveNamesIt('has the right name', function () {
- expect(Set.prototype.forEach).to.have.property('name', 'forEach');
- });
- it('is not enumerable', function () {
- expect(Set.prototype).ownPropertyDescriptor('forEach').to.have.property('enumerable', false);
- });
- it('has the right arity', function () {
- expect(Set.prototype.forEach).to.have.property('length', 1);
- });
- it('should be iterable via forEach', function () {
- var expectedSet = ['a', 'b', 'c'];
- var foundSet = [];
- setToIterate.forEach(function (value, alsoValue, entireSet) {
- expect(entireSet).to.equal(setToIterate);
- expect(value).to.equal(alsoValue);
- foundSet.push(value);
- });
- expect(foundSet).to.eql(expectedSet);
- });
- it('should iterate over empty keys', function () {
- var setWithEmptyKeys = new Set();
- var expectedKeys = [{}, null, undefined, '', NaN, 0];
- expectedKeys.forEach(function (key) {
- expect(setWithEmptyKeys.add(key)).to.equal(setWithEmptyKeys);
- });
- var foundKeys = [];
- setWithEmptyKeys.forEach(function (value, key, entireSet) {
- expect([key]).to.be.theSameSet([value]); // handles NaN correctly
- expect(entireSet.has(key)).to.equal(true);
- foundKeys.push(key);
- });
- expect(foundKeys).to.be.theSameSet(expectedKeys);
- });
- it('should support the thisArg', function () {
- var context = function () {};
- setToIterate.forEach(function () {
- expect(this).to.equal(context);
- }, context);
- });
- it('should have a length of 1', function () {
- expect(Set.prototype.forEach.length).to.equal(1);
- });
- it('should not revisit modified keys', function () {
- var hasModifiedA = false;
- setToIterate.forEach(function (value, key) {
- if (!hasModifiedA && key === 'a') {
- expect(setToIterate.add('a')).to.equal(setToIterate);
- hasModifiedA = true;
- } else {
- expect(key).not.to.equal('a');
- }
- });
- });
- it('visits keys added in the iterator', function () {
- var hasAdded = false;
- var hasFoundD = false;
- setToIterate.forEach(function (value, key) {
- if (!hasAdded) {
- expect(setToIterate.add('d')).to.equal(setToIterate);
- hasAdded = true;
- } else if (key === 'd') {
- hasFoundD = true;
- }
- });
- expect(hasFoundD).to.equal(true);
- });
- it('visits keys added in the iterator when there is a deletion (slow path)', function () {
- var hasSeenFour = false;
- var setToMutate = new Set();
- expect(setToMutate.add({})).to.equal(setToMutate); // force use of the slow O(N) implementation
- expect(setToMutate.add('0')).to.equal(setToMutate);
- setToMutate.forEach(function (value, key) {
- if (key === '0') {
- expect(setToMutate['delete']('0')).to.equal(true);
- expect(setToMutate.add('4')).to.equal(setToMutate);
- } else if (key === '4') {
- hasSeenFour = true;
- }
- });
- expect(hasSeenFour).to.equal(true);
- });
- it('visits keys added in the iterator when there is a deletion (fast path)', function () {
- var hasSeenFour = false;
- var setToMutate = new Set();
- expect(setToMutate.add('0')).to.equal(setToMutate);
- setToMutate.forEach(function (value, key) {
- if (key === '0') {
- expect(setToMutate['delete']('0')).to.equal(true);
- expect(setToMutate.add('4')).to.equal(setToMutate);
- } else if (key === '4') {
- hasSeenFour = true;
- }
- });
- expect(hasSeenFour).to.equal(true);
- });
- it('does not visit keys deleted before a visit', function () {
- var hasVisitedC = false;
- var hasDeletedC = false;
- setToIterate.forEach(function (value, key) {
- if (key === 'c') {
- hasVisitedC = true;
- }
- if (!hasVisitedC && !hasDeletedC) {
- hasDeletedC = setToIterate['delete']('c');
- expect(hasDeletedC).to.equal(true);
- }
- });
- expect(hasVisitedC).to.equal(false);
- });
- it('should work after deletion of the current key', function () {
- var expectedSet = {
- a: 'a',
- b: 'b',
- c: 'c'
- };
- var foundSet = {};
- setToIterate.forEach(function (value, key) {
- foundSet[key] = value;
- expect(setToIterate['delete'](key)).to.equal(true);
- });
- expect(foundSet).to.eql(expectedSet);
- });
- it('should convert key -0 to +0', function () {
- var zeroSet = new Set();
- var result = [];
- expect(zeroSet.add(-0)).to.equal(zeroSet);
- zeroSet.forEach(function (key) {
- result.push(String(1 / key));
- });
- expect(zeroSet.add(1)).to.equal(zeroSet);
- expect(zeroSet.add(0)).to.equal(zeroSet); // shouldn't cause reordering
- zeroSet.forEach(function (key) {
- result.push(String(1 / key));
- });
- expect(result.join(', ')).to.equal('Infinity, Infinity, 1');
- });
- });
- it('Set.prototype.size should throw TypeError', function () {
- // see https://github.com/paulmillr/es6-shim/issues/176
- expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
- expect(function () { return Set.prototype.size; }).to['throw'](TypeError);
- });
- it.skip('should throw proper errors when user invokes methods with wrong types of receiver', function () {
- });
- ifGetPrototypeOfIt('SetIterator identification test prototype inequality', function () {
- var mapEntriesProto = Object.getPrototypeOf(new Map().entries());
- var setEntriesProto = Object.getPrototypeOf(new Set().entries());
- expect(mapEntriesProto).to.not.equal(setEntriesProto);
- });
- it('SetIterator identification', function () {
- var fnSetValues = Set.prototype.values;
- var setSentinel = new Set(['SetSentinel']);
- var testSet1 = new Set();
- var testSetValues = testSet1.values();
- expect(testSetValues.next.call(fnSetValues.call(setSentinel)).value).to.equal('SetSentinel');
- var testMap = new Map();
- var testMapValues = testMap.values();
- expect(function () {
- return testMapValues.next.call(fnSetValues.call(setSentinel)).value;
- }).to['throw'](TypeError);
- });
- });
|