diff --git a/CHANGELOG.md b/CHANGELOG.md index f20ecdf7005c..d5ce626f9891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## Changelog ##### Unreleased +- Added [relative indexing method stage 3 proposal](https://github.com/tc39/proposal-relative-indexing-method) + - `Array#at` + - `%TypedArray%#at` - Added [`Number.range` stage 1 proposal](https://github.com/tc39/proposal-Number.range) - `Number.range` - `BigInt.range` @@ -8,6 +11,7 @@ - `%TypedArray%#filterOut` - Added [array deduplication stage 1 proposal](https://github.com/tc39/proposal-array-unique) - `Array#uniqueBy` +- Added code points / code units explicit feature detection in `String#at` for preventing breakage code which use obsolete `String#at` proposal polyfill - Added the missed `(es|stable)/instance/replace-all` entries - Updated compat data mapping for Opera - from Opera 69, the difference with Chrome versions increased to 14 - Compat data mapping for modern Android WebView to Chrome moved from targets parser directly to compat data diff --git a/README.md b/README.md index 9a2794f4cad4..33da0f587750 100644 --- a/README.md +++ b/README.md @@ -1778,7 +1778,40 @@ Promise.any([ core-js(-pure)/stage/3 ``` -None. +##### [Relative indexing method](https://github.com/tc39/proposal-relative-indexing-method)[⬆](#index) +Modules [`esnext.array.at`](https://github.com/zloirock/core-js/blob/v3.7.0/packages/core-js/modules/esnext.array.at.js) and [`esnext.typed-array.at`](https://github.com/zloirock/core-js/blob/v3.7.0/packages/core-js/modules/esnext.typed-array.at.js) +> **Warning! Because of the conflict with [another proposal](#stringat), this method is not available on `String.prototype` in this version.** + +```js +class Array { + at(index: int): any; +} + +class [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array, +] { + at(index: int): any; +} +``` +[*CommonJS entry points:*](#commonjs-api) +``` +core-js/proposals/relative-indexing-method +core-js(-pure)/features/array/at +core-js(-pure)/features/typed-array/at +``` +[*Examples*](http://es6.zloirock.ru/#log(%5B1%2C%202%2C%203%5D.at(1))%3B%20%20%2F%2F%20%3D%3E%202%0Alog(%5B1%2C%202%2C%203%5D.at(-1))%3B%20%2F%2F%20%3D%3E%203): +```js +[1, 2, 3].at(1); // => 2 +[1, 2, 3].at(-1); // => 3 +``` #### Stage 2 proposals[⬆](#index) [*CommonJS entry points:*](#commonjs-api) diff --git a/packages/core-js-compat/src/data.js b/packages/core-js-compat/src/data.js index a604eaf6ab27..c30d045b7c10 100644 --- a/packages/core-js-compat/src/data.js +++ b/packages/core-js-compat/src/data.js @@ -1258,6 +1258,8 @@ const data = { }, 'esnext.array.is-template-object': { }, + 'esnext.array.at': { + }, 'esnext.array.last-index': { }, 'esnext.array.last-item': { @@ -1492,6 +1494,8 @@ const data = { // TODO: Remove from `core-js@4` 'esnext.symbol.replace-all': { }, + 'esnext.typed-array.at': { + }, 'esnext.typed-array.filter-out': { }, 'esnext.weak-map.delete-all': { diff --git a/packages/core-js-compat/src/modules-by-versions.js b/packages/core-js-compat/src/modules-by-versions.js index f01cbf3377d7..83485ff63626 100644 --- a/packages/core-js-compat/src/modules-by-versions.js +++ b/packages/core-js-compat/src/modules-by-versions.js @@ -65,10 +65,12 @@ module.exports = { 'esnext.weak-map.emplace', ], 3.8: [ + 'esnext.array.at', 'esnext.array.filter-out', 'esnext.array.unique-by', 'esnext.bigint.range', 'esnext.number.range', + 'esnext.typed-array.at', 'esnext.typed-array.filter-out', ], }; diff --git a/packages/core-js-pure/override/modules/esnext.typed-array.at.js b/packages/core-js-pure/override/modules/esnext.typed-array.at.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/esnext.typed-array.at.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js/features/array/at.js b/packages/core-js/features/array/at.js new file mode 100644 index 000000000000..9b923f612724 --- /dev/null +++ b/packages/core-js/features/array/at.js @@ -0,0 +1,4 @@ +require('../../modules/esnext.array.at'); +var entryUnbind = require('../../internals/entry-unbind'); + +module.exports = entryUnbind('Array', 'at'); diff --git a/packages/core-js/features/array/index.js b/packages/core-js/features/array/index.js index 150190dffd15..eecc4b31cde9 100644 --- a/packages/core-js/features/array/index.js +++ b/packages/core-js/features/array/index.js @@ -1,5 +1,6 @@ var parent = require('../../es/array'); require('../../modules/es.map'); +require('../../modules/esnext.array.at'); require('../../modules/esnext.array.filter-out'); require('../../modules/esnext.array.is-template-object'); require('../../modules/esnext.array.last-item'); diff --git a/packages/core-js/features/array/virtual/at.js b/packages/core-js/features/array/virtual/at.js new file mode 100644 index 000000000000..14f8e5aff59d --- /dev/null +++ b/packages/core-js/features/array/virtual/at.js @@ -0,0 +1,4 @@ +require('../../../modules/esnext.array.at'); +var entryVirtual = require('../../../internals/entry-virtual'); + +module.exports = entryVirtual('Array').at; diff --git a/packages/core-js/features/array/virtual/index.js b/packages/core-js/features/array/virtual/index.js index cb6a41868695..6572f923dc93 100644 --- a/packages/core-js/features/array/virtual/index.js +++ b/packages/core-js/features/array/virtual/index.js @@ -1,4 +1,6 @@ var parent = require('../../../es/array/virtual'); +require('../../../modules/esnext.array.at'); require('../../../modules/esnext.array.filter-out'); +require('../../../modules/esnext.array.unique-by'); module.exports = parent; diff --git a/packages/core-js/features/instance/at.js b/packages/core-js/features/instance/at.js index f9b4f7986dcb..736ff4d34bc6 100644 --- a/packages/core-js/features/instance/at.js +++ b/packages/core-js/features/instance/at.js @@ -1,9 +1,13 @@ -var at = require('../string/virtual/at'); +var arrayAt = require('../array/virtual/at'); +var stringAt = require('../string/virtual/at'); +var ArrayPrototype = Array.prototype; var StringPrototype = String.prototype; module.exports = function (it) { var own = it.at; - return typeof it === 'string' || it === StringPrototype - || (it instanceof String && own === StringPrototype.at) ? at : own; + if (it === ArrayPrototype || (it instanceof Array && own === ArrayPrototype.at)) return arrayAt; + if (typeof it === 'string' || it === StringPrototype || (it instanceof String && own === StringPrototype.at)) { + return stringAt; + } return own; }; diff --git a/packages/core-js/features/string/index.js b/packages/core-js/features/string/index.js index 7c7f167cf834..28e5520c77da 100644 --- a/packages/core-js/features/string/index.js +++ b/packages/core-js/features/string/index.js @@ -1,5 +1,7 @@ var parent = require('../../es/string'); require('../../modules/esnext.string.at'); +// TODO: disabled by default because of the conflict with another proposal +// require('../../modules/esnext.string.at-alternative'); require('../../modules/esnext.string.code-points'); // TODO: remove from `core-js@4` require('../../modules/esnext.string.match-all'); diff --git a/packages/core-js/features/string/virtual/index.js b/packages/core-js/features/string/virtual/index.js index 50044779e648..aaef5105ec36 100644 --- a/packages/core-js/features/string/virtual/index.js +++ b/packages/core-js/features/string/virtual/index.js @@ -1,5 +1,7 @@ var parent = require('../../../es/string/virtual'); require('../../../modules/esnext.string.at'); +// TODO: disabled by default because of the conflict with another proposal +// require('../../../modules/esnext.string.at-alternative'); require('../../../modules/esnext.string.code-points'); // TODO: remove from `core-js@4` require('../../../modules/esnext.string.match-all'); diff --git a/packages/core-js/features/typed-array/at.js b/packages/core-js/features/typed-array/at.js new file mode 100644 index 000000000000..4882b7edbd19 --- /dev/null +++ b/packages/core-js/features/typed-array/at.js @@ -0,0 +1 @@ +require('../../modules/esnext.typed-array.at'); diff --git a/packages/core-js/features/typed-array/index.js b/packages/core-js/features/typed-array/index.js index 4ac587fca23a..db032de1171f 100644 --- a/packages/core-js/features/typed-array/index.js +++ b/packages/core-js/features/typed-array/index.js @@ -1,4 +1,5 @@ var parent = require('../../es/typed-array'); +require('../../modules/esnext.typed-array.at'); require('../../modules/esnext.typed-array.filter-out'); module.exports = parent; diff --git a/packages/core-js/modules/esnext.array.at.js b/packages/core-js/modules/esnext.array.at.js new file mode 100644 index 000000000000..0342f850617b --- /dev/null +++ b/packages/core-js/modules/esnext.array.at.js @@ -0,0 +1,20 @@ +'use strict'; +var $ = require('../internals/export'); +var toObject = require('../internals/to-object'); +var toLength = require('../internals/to-length'); +var toInteger = require('../internals/to-integer'); +var addToUnscopables = require('../internals/add-to-unscopables'); + +// `Array.prototype.at` method +// https://github.com/tc39/proposal-relative-indexing-method +$({ target: 'Array', proto: true }, { + at: function at(index) { + var O = toObject(this); + var len = toLength(O.length); + var relativeIndex = toInteger(index); + var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + return (k < 0 || k >= len) ? undefined : O[k]; + } +}); + +addToUnscopables('at'); diff --git a/packages/core-js/modules/esnext.string.at-alternative.js b/packages/core-js/modules/esnext.string.at-alternative.js new file mode 100644 index 000000000000..5158e2e75594 --- /dev/null +++ b/packages/core-js/modules/esnext.string.at-alternative.js @@ -0,0 +1,23 @@ +// TODO: disabled by default because of the conflict with another proposal +'use strict'; +var $ = require('../internals/export'); +var requireObjectCoercible = require('../internals/require-object-coercible'); +var toLength = require('../internals/to-length'); +var toInteger = require('../internals/to-integer'); +var fails = require('../internals/fails'); + +var FORCED = fails(function () { + return '𠮷'.at(0) !== '\uD842'; +}); + +// `String.prototype.at` method +// https://github.com/tc39/proposal-relative-indexing-method +$({ target: 'String', proto: true, forced: FORCED }, { + at: function at(index) { + var S = String(requireObjectCoercible(this)); + var len = toLength(S.length); + var relativeIndex = toInteger(index); + var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + return (k < 0 || k >= len) ? undefined : S.charAt(k); + } +}); diff --git a/packages/core-js/modules/esnext.string.at.js b/packages/core-js/modules/esnext.string.at.js index fc3c44a76063..be0ce4049a40 100644 --- a/packages/core-js/modules/esnext.string.at.js +++ b/packages/core-js/modules/esnext.string.at.js @@ -1,10 +1,15 @@ 'use strict'; var $ = require('../internals/export'); var charAt = require('../internals/string-multibyte').charAt; +var fails = require('../internals/fails'); + +var FORCED = fails(function () { + return '𠮷'.at(0) !== '𠮷'; +}); // `String.prototype.at` method // https://github.com/mathiasbynens/String.prototype.at -$({ target: 'String', proto: true }, { +$({ target: 'String', proto: true, forced: FORCED }, { at: function at(pos) { return charAt(this, pos); } diff --git a/packages/core-js/modules/esnext.typed-array.at.js b/packages/core-js/modules/esnext.typed-array.at.js new file mode 100644 index 000000000000..f6da023cb98d --- /dev/null +++ b/packages/core-js/modules/esnext.typed-array.at.js @@ -0,0 +1,17 @@ +'use strict'; +var ArrayBufferViewCore = require('../internals/array-buffer-view-core'); +var toLength = require('../internals/to-length'); +var toInteger = require('../internals/to-integer'); + +var aTypedArray = ArrayBufferViewCore.aTypedArray; +var exportTypedArrayMethod = ArrayBufferViewCore.exportTypedArrayMethod; + +// `%TypedArray%.prototype.at` method +// https://github.com/tc39/proposal-relative-indexing-method +exportTypedArrayMethod('at', function at(index) { + var O = aTypedArray(this); + var len = toLength(O.length); + var relativeIndex = toInteger(index); + var k = relativeIndex >= 0 ? relativeIndex : len + relativeIndex; + return (k < 0 || k >= len) ? undefined : O[k]; +}); diff --git a/packages/core-js/proposals/relative-indexing-method.js b/packages/core-js/proposals/relative-indexing-method.js new file mode 100644 index 000000000000..95e681400b58 --- /dev/null +++ b/packages/core-js/proposals/relative-indexing-method.js @@ -0,0 +1,5 @@ +// https://github.com/tc39/proposal-relative-indexing-method +require('../modules/esnext.array.at'); +// TODO: disabled by default because of the conflict with another proposal +// require('../modules/esnext.string.at-alternative'); +require('../modules/esnext.typed-array.at'); diff --git a/packages/core-js/stage/3.js b/packages/core-js/stage/3.js index 17c9a4db1172..7f4ea8778a97 100644 --- a/packages/core-js/stage/3.js +++ b/packages/core-js/stage/3.js @@ -1,3 +1,4 @@ +require('../proposals/relative-indexing-method'); var parent = require('./4'); module.exports = parent; diff --git a/tests/commonjs.js b/tests/commonjs.js index c235c83f621f..5866dbe0ddf9 100644 --- a/tests/commonjs.js +++ b/tests/commonjs.js @@ -62,6 +62,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(typeof load('features/array/is-template-object') === 'function'); ok(Array.isArray(load('features/array/from')('qwe'))); ok(Array.isArray(load('features/array/of')('q', 'w', 'e'))); + ok(load('features/array/at')([1, 2, 3], -2) === 2); ok(load('features/array/join')('qwe', 1) === 'q1w1e'); ok(load('features/array/slice')('qwe', 1)[1] === 'e'); ok(load('features/array/sort')([1, 3, 2])[1] === 2); @@ -92,6 +93,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/array/includes')([1, 2, 3], 2)); ok('next' in load('features/array/iterator')([])); ok(typeof load('features/array/unique-by') === 'function'); + ok(load('features/array/virtual/at').call([1, 2, 3], -2) === 2); ok(load('features/array/virtual/join').call('qwe', 1) === 'q1w1e'); ok(load('features/array/virtual/slice').call('qwe', 1)[1] === 'e'); ok(load('features/array/virtual/splice').call([1, 2, 3], 1, 2)[0] === 2); @@ -210,6 +212,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/string/code-point-at')('a', 0) === 97); ok(load('features/string/ends-with')('qwe', 'we')); ok(load('features/string/includes')('qwe', 'w')); + // ok(load('features/string/at-alternative')('123', -2) === '2'); ok(load('features/string/repeat')('q', 3) === 'qqq'); ok(load('features/string/starts-with')('qwe', 'qw')); ok(typeof load('features/string/anchor') === 'function'); @@ -239,6 +242,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { ok(load('features/string/virtual/code-point-at').call('a', 0) === 97); ok(load('features/string/virtual/ends-with').call('qwe', 'we')); ok(load('features/string/virtual/includes').call('qwe', 'w')); + // ok(load('features/string/virtual/at-alternative').call('123', -2) === '2'); ok(load('features/string/virtual/repeat').call('q', 3) === 'qqq'); ok(load('features/string/virtual/starts-with').call('qwe', 'qw')); ok(typeof load('features/string/virtual/anchor') === 'function'); @@ -977,6 +981,7 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { load('proposals/promise-any'); load('proposals/promise-try'); load('proposals/reflect-metadata'); + load('proposals/relative-indexing-method'); load('proposals/keys-composition'); load('proposals/seeded-random'); load('proposals/set-methods'); @@ -999,8 +1004,10 @@ for (const _PATH of ['../packages/core-js-pure', '../packages/core-js']) { const instanceAt = load('features/instance/at'); ok(typeof instanceAt === 'function'); ok(instanceAt({}) === undefined); + ok(typeof instanceAt([]) === 'function'); ok(typeof instanceAt('') === 'function'); - ok(instanceAt('').call('abc', 1) === 'b'); + ok(instanceAt([]).call([1, 2, 3], 2) === 3); + ok(instanceAt('').call('123', 2) === '3'); let instanceBind = load('features/instance/bind'); ok(typeof instanceBind === 'function'); @@ -1653,6 +1660,7 @@ ok(typeof load('features/typed-array/int32-array') === 'function'); ok(typeof load('features/typed-array/uint32-array') === 'function'); ok(typeof load('features/typed-array/float32-array') === 'function'); ok(typeof load('features/typed-array/float64-array') === 'function'); +load('features/typed-array/at'); load('features/typed-array/copy-within'); load('features/typed-array/entries'); load('features/typed-array/every'); diff --git a/tests/compat/tests.js b/tests/compat/tests.js index df59e89f0f1e..9a8cded1617e 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -1100,6 +1100,9 @@ GLOBAL.tests = { && set.add({}) == set && set[Symbol.toStringTag]; }], + 'esnext.array.at': function () { + return [].at; + }, 'esnext.array.filter-out': function () { return [].filterOut; }, @@ -1422,7 +1425,7 @@ GLOBAL.tests = { return Set.prototype.union; }, 'esnext.string.at': function () { - return String.prototype.at; + return '𠮷'.at(0) === '𠮷'; }, 'esnext.string.code-points': function () { return String.prototype.codePoints; @@ -1440,6 +1443,9 @@ GLOBAL.tests = { 'esnext.symbol.replace-all': function () { return Symbol.replaceAll; }, + 'esnext.typed-array.at': function () { + return Int8Array.prototype.at; + }, 'esnext.typed-array.filter-out': function () { return Int8Array.prototype.filterOut; }, diff --git a/tests/pure/esnext.array.at.js b/tests/pure/esnext.array.at.js new file mode 100644 index 000000000000..926f967626b2 --- /dev/null +++ b/tests/pure/esnext.array.at.js @@ -0,0 +1,27 @@ +import { STRICT } from '../helpers/constants'; + +import at from 'core-js-pure/features/array/at'; + +QUnit.test('Array#at', assert => { + assert.isFunction(at); + assert.same(1, at([1, 2, 3], 0)); + assert.same(2, at([1, 2, 3], 1)); + assert.same(3, at([1, 2, 3], 2)); + assert.same(undefined, at([1, 2, 3], 3)); + assert.same(3, at([1, 2, 3], -1)); + assert.same(2, at([1, 2, 3], -2)); + assert.same(1, at([1, 2, 3], -3)); + assert.same(undefined, at([1, 2, 3], -4)); + assert.same(1, at([1, 2, 3], 0.4)); + assert.same(1, at([1, 2, 3], 0.5)); + assert.same(1, at([1, 2, 3], 0.6)); + assert.same(1, at([1], NaN)); + assert.same(1, at([1])); + assert.same(1, at([1, 2, 3], -0)); + assert.same(undefined, at(Array(1), 0)); + assert.same(1, at({ 0: 1, length: 1 }, 0)); + if (STRICT) { + assert.throws(() => at(null, 0), TypeError); + assert.throws(() => at(undefined, 0), TypeError); + } +}); diff --git a/tests/pure/esnext.string.at-alternative.js b/tests/pure/esnext.string.at-alternative.js new file mode 100644 index 000000000000..834298592aaf --- /dev/null +++ b/tests/pure/esnext.string.at-alternative.js @@ -0,0 +1,28 @@ +// TODO: disabled by default because of the conflict with another proposal +import { STRICT } from '../helpers/constants'; + +import at from 'core-js-pure/features/string/at'; + +QUnit.test('String#at', assert => { + assert.isFunction(at); + assert.same('1', at('123', 0)); + assert.same('2', at('123', 1)); + assert.same('3', at('123', 2)); + assert.same(undefined, at('123', 3)); + assert.same('3', at('123', -1)); + assert.same('2', at('123', -2)); + assert.same('1', at('123', -3)); + assert.same(undefined, at('123', -4)); + assert.same('1', at('123', 0.4)); + assert.same('1', at('123', 0.5)); + assert.same('1', at('123', 0.6)); + assert.same('1', at('1', NaN)); + assert.same('1', at('1')); + assert.same('1', at('123', -0)); + assert.same('\uD842', at('𠮷')); + assert.same('1', at({ toString() { return '123'; } }, 0)); + if (STRICT) { + assert.throws(() => at(null, 0), TypeError); + assert.throws(() => at(undefined, 0), TypeError); + } +}); diff --git a/tests/pure/index.js b/tests/pure/index.js index 1aa7700a15d3..604eca82ff26 100644 --- a/tests/pure/index.js +++ b/tests/pure/index.js @@ -143,6 +143,7 @@ import './es.weak-map'; import './es.weak-set'; QUnit.module('ESNext'); +import './esnext.array.at'; import './esnext.array.filter-out'; import './esnext.array.is-template-object'; import './esnext.array.unique-by'; @@ -245,6 +246,8 @@ import './esnext.set.some'; import './esnext.set.symmetric-difference'; import './esnext.set.union'; import './esnext.string.at'; +// TODO: disabled by default because of the conflict with another proposal +// import './esnext.string.at-alternative'; import './esnext.string.code-points'; import './esnext.symbol.async-dispose'; import './esnext.symbol.dispose'; diff --git a/tests/tests/esnext.array.at.js b/tests/tests/esnext.array.at.js new file mode 100644 index 000000000000..f0ea6572fd3c --- /dev/null +++ b/tests/tests/esnext.array.at.js @@ -0,0 +1,30 @@ +import { STRICT } from '../helpers/constants'; + +QUnit.test('Array#at', assert => { + const { at } = Array.prototype; + assert.isFunction(at); + assert.arity(at, 1); + assert.name(at, 'at'); + assert.looksNative(at); + assert.nonEnumerable(Array.prototype, 'at'); + assert.same(1, [1, 2, 3].at(0)); + assert.same(2, [1, 2, 3].at(1)); + assert.same(3, [1, 2, 3].at(2)); + assert.same(undefined, [1, 2, 3].at(3)); + assert.same(3, [1, 2, 3].at(-1)); + assert.same(2, [1, 2, 3].at(-2)); + assert.same(1, [1, 2, 3].at(-3)); + assert.same(undefined, [1, 2, 3].at(-4)); + assert.same(1, [1, 2, 3].at(0.4)); + assert.same(1, [1, 2, 3].at(0.5)); + assert.same(1, [1, 2, 3].at(0.6)); + assert.same(1, [1].at(NaN)); + assert.same(1, [1].at()); + assert.same(1, [1, 2, 3].at(-0)); + assert.same(undefined, Array(1).at(0)); + assert.same(1, at.call({ 0: 1, length: 1 }, 0)); + if (STRICT) { + assert.throws(() => at.call(null, 0), TypeError); + assert.throws(() => at.call(undefined, 0), TypeError); + } +}); diff --git a/tests/tests/esnext.string.at-alternative.js b/tests/tests/esnext.string.at-alternative.js new file mode 100644 index 000000000000..e84bcf3fadea --- /dev/null +++ b/tests/tests/esnext.string.at-alternative.js @@ -0,0 +1,31 @@ +// TODO: disabled by default because of the conflict with another proposal +import { STRICT } from '../helpers/constants'; + +QUnit.test('String#at', assert => { + const { at } = String.prototype; + assert.isFunction(at); + assert.arity(at, 1); + assert.name(at, 'at'); + assert.looksNative(at); + assert.nonEnumerable(String.prototype, 'at'); + assert.same('1', '123'.at(0)); + assert.same('2', '123'.at(1)); + assert.same('3', '123'.at(2)); + assert.same(undefined, '123'.at(3)); + assert.same('3', '123'.at(-1)); + assert.same('2', '123'.at(-2)); + assert.same('1', '123'.at(-3)); + assert.same(undefined, '123'.at(-4)); + assert.same('1', '123'.at(0.4)); + assert.same('1', '123'.at(0.5)); + assert.same('1', '123'.at(0.6)); + assert.same('1', '1'.at(NaN)); + assert.same('1', '1'.at()); + assert.same('1', '123'.at(-0)); + assert.same('\uD842', '𠮷'.at()); + assert.same('1', at.call({ toString() { return '123'; } }, 0)); + if (STRICT) { + assert.throws(() => at.call(null, 0), TypeError); + assert.throws(() => at.call(undefined, 0), TypeError); + } +}); diff --git a/tests/tests/esnext.typed-array.at.js b/tests/tests/esnext.typed-array.at.js new file mode 100644 index 000000000000..df17e436eed3 --- /dev/null +++ b/tests/tests/esnext.typed-array.at.js @@ -0,0 +1,30 @@ +import { DESCRIPTORS, GLOBAL, TYPED_ARRAYS } from '../helpers/constants'; + +if (DESCRIPTORS) QUnit.test('%TypedArrayPrototype%.indexOf', assert => { + // we can't implement %TypedArrayPrototype% in all engines, so run all tests for each typed array constructor + for (const name in TYPED_ARRAYS) { + const TypedArray = GLOBAL[name]; + const { at } = TypedArray.prototype; + assert.isFunction(at); + assert.arity(at, 1); + assert.name(at, 'at'); + assert.looksNative(at); + assert.same(1, new TypedArray([1, 2, 3]).at(0)); + assert.same(2, new TypedArray([1, 2, 3]).at(1)); + assert.same(3, new TypedArray([1, 2, 3]).at(2)); + assert.same(undefined, new TypedArray([1, 2, 3]).at(3)); + assert.same(3, new TypedArray([1, 2, 3]).at(-1)); + assert.same(2, new TypedArray([1, 2, 3]).at(-2)); + assert.same(1, new TypedArray([1, 2, 3]).at(-3)); + assert.same(undefined, new TypedArray([1, 2, 3]).at(-4)); + assert.same(1, new TypedArray([1, 2, 3]).at(0.4)); + assert.same(1, new TypedArray([1, 2, 3]).at(0.5)); + assert.same(1, new TypedArray([1, 2, 3]).at(0.6)); + assert.same(1, new TypedArray([1]).at(NaN)); + assert.same(1, new TypedArray([1]).at()); + assert.same(1, new TypedArray([1, 2, 3]).at(-0)); + assert.throws(() => at.call({ 0: 1, length: 1 }, 0), TypeError); + assert.throws(() => at.call(null, 0), TypeError); + assert.throws(() => at.call(undefined, 0), TypeError); + } +}); diff --git a/tests/tests/index.js b/tests/tests/index.js index 7ec3f696bbe1..02cd2672444d 100644 --- a/tests/tests/index.js +++ b/tests/tests/index.js @@ -199,6 +199,7 @@ import './es.weak-set'; QUnit.module('ESNext'); import './esnext.array.filter-out'; import './esnext.array.is-template-object'; +import './esnext.array.at'; import './esnext.array.last-item'; import './esnext.array.last-index'; import './esnext.array.unique-by'; @@ -301,12 +302,15 @@ import './esnext.set.some'; import './esnext.set.symmetric-difference'; import './esnext.set.union'; import './esnext.string.at'; +// TODO: disabled by default because of the conflict with another proposal +// import './esnext.string.at-alternative'; import './esnext.string.code-points'; import './esnext.symbol.async-dispose'; import './esnext.symbol.dispose'; import './esnext.symbol.observable'; import './esnext.symbol.pattern-match'; import './esnext.symbol.replace-all'; +import './esnext.typed-array.at'; import './esnext.typed-array.filter-out'; import './esnext.weak-map.delete-all'; import './esnext.weak-map.from';