array.js 38 KB

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