diff --git a/.github/workflows/_local-not-for-reuse-ci.yml b/.github/workflows/_local-not-for-reuse-ci.yml index ab8ce7e4..36ed0483 100644 --- a/.github/workflows/_local-not-for-reuse-ci.yml +++ b/.github/workflows/_local-not-for-reuse-ci.yml @@ -34,34 +34,14 @@ jobs: test-npm-packages: name: Test NPM Packages runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 15 steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - autocrlf: false - persist-credentials: false - - uses: SocketDev/socket-registry/.github/actions/setup-and-install@bab1dc8adc59a21c04fa73d3dd4a333c12e2ca9b # main with: node-version: 22 - socket-api-key: ${{ secrets.SOCKET_API_KEY }} - - - uses: SocketDev/socket-registry/.github/actions/cache-npm-packages@bab1dc8adc59a21c04fa73d3dd4a333c12e2ca9b # main - - name: Verify sfw is installed - shell: bash - run: | - if [ -z "$SFW_BIN" ] || [ ! -x "$SFW_BIN" ]; then - echo "Error: sfw is not installed — run SocketDev/socket-registry/.github/actions/setup first" >&2 - exit 1 - fi - - - name: Build registry + - name: Build and test npm packages shell: bash run: | pnpm run build - - - name: Run npm package tests - shell: bash - run: | - node scripts/npm/test-npm-packages.mjs + pnpm run test:npm diff --git a/package.json b/package.json index d7ad7467..632c1bd9 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "publish:ci": "node scripts/publish.mjs --skip-build --tag ${DIST_TAG:-latest}", "release-npm": "node scripts/npm/release-npm-packages.mjs", "test": "node scripts/test.mjs", + "test:npm": "node scripts/npm/run-vitest-npm.mjs", "update": "node scripts/update.mjs", "validate-ci": "node scripts/testing/reproduce-ci-locally.mjs", "validate-packages": "node scripts/testing/validate-package-tests.mjs" diff --git a/scripts/npm/run-vitest-npm.mjs b/scripts/npm/run-vitest-npm.mjs new file mode 100644 index 00000000..c9581574 --- /dev/null +++ b/scripts/npm/run-vitest-npm.mjs @@ -0,0 +1,15 @@ +#!/usr/bin/env node +// Run vitest on test/npm/ packages. +// Sets INCLUDE_NPM_TESTS so vitest config includes test/npm/. +import { spawn } from '@socketsecurity/lib/spawn' + +process.env['INCLUDE_NPM_TESTS'] = '1' +const result = await spawn( + 'pnpm', + ['exec', 'vitest', 'run', 'test/npm/', ...process.argv.slice(2)], + { + shell: process.platform === 'win32', + stdio: 'inherit', + }, +) +process.exitCode = result.code ?? 0 diff --git a/scripts/npm/test-npm-packages.mjs b/scripts/npm/test-npm-packages.mjs index 51e1f355..7ad6d603 100755 --- a/scripts/npm/test-npm-packages.mjs +++ b/scripts/npm/test-npm-packages.mjs @@ -22,15 +22,15 @@ const { values: cliArgs } = parseArgs({ }, 'download-concurrency': { type: 'string', - default: getCI() ? (WIN32 ? '10' : '20') : '50', + default: getCI() ? '3' : '20', }, 'install-concurrency': { type: 'string', - default: getCI() ? (WIN32 ? '5' : '10') : '30', + default: getCI() ? '3' : '10', }, 'test-concurrency': { type: 'string', - default: getCI() ? (WIN32 ? '3' : '8') : '40', + default: getCI() ? '3' : '20', }, force: { type: 'boolean', diff --git a/scripts/utils/package.mjs b/scripts/utils/package.mjs index a1be36f5..53fc0887 100644 --- a/scripts/utils/package.mjs +++ b/scripts/utils/package.mjs @@ -371,14 +371,24 @@ export async function installPackageForTesting( ), ) + // Write pnpm-workspace.yaml with v11 workarounds. + await fs.writeFile( + path.join(packageTempDir, 'pnpm-workspace.yaml'), + 'packages:\n - .\n\nregistrySupportsTimeField: false\n', + ) + // Install the package. const packageSpec = versionSpec.startsWith('https://') ? versionSpec : `${packageName}@${versionSpec}` - await runCommand('pnpm', ['add', packageSpec, ...PNPM_NPM_LIKE_FLAGS], { - cwd: packageTempDir, - }) + await runCommand( + PNPM_REAL_BIN, + ['add', packageSpec, ...PNPM_HOISTED_INSTALL_FLAGS], + { + cwd: packageTempDir, + }, + ) installedPath = path.join(packageTempDir, 'node_modules', packageName) @@ -496,9 +506,13 @@ export async function installPackageForTesting( await fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2)) // Install dependencies with pnpm. - await runCommand('pnpm', ['install', ...PNPM_HOISTED_INSTALL_FLAGS], { - cwd: installedPath, - }) + await runCommand( + PNPM_REAL_BIN, + ['install', ...PNPM_HOISTED_INSTALL_FLAGS], + { + cwd: installedPath, + }, + ) return { installed: true, diff --git a/test/npm/aggregate-error.test.mts b/test/npm/aggregate-error.test.mts index 7cdb3574..e1ad4374 100644 --- a/test/npm/aggregate-error.test.mts +++ b/test/npm/aggregate-error.test.mts @@ -19,7 +19,8 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { const aggregateError = new AggregateError(errors) expect(aggregateError).toBeInstanceOf(Error) - expect(aggregateError.message).toBe('') + expect(aggregateError.message).toContain('error 1') + expect(aggregateError.message).toContain('error 2') expect(Array.isArray(aggregateError.errors)).toBe(true) expect(aggregateError.errors.length).toBe(2) }) diff --git a/test/npm/array.of.test.mts b/test/npm/array.of.test.mts index b741023d..ab880bf5 100644 --- a/test/npm/array.of.test.mts +++ b/test/npm/array.of.test.mts @@ -153,7 +153,19 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { }, }) - const expected = Object.assign(new MyType(), { 0: 'abc', length: 1 }) + const expected = new MyType() + Object.defineProperty(expected, '0', { + value: 'abc', + writable: true, + enumerable: true, + configurable: true, + }) + Object.defineProperty(expected, 'length', { + value: 1, + writable: true, + enumerable: true, + configurable: true, + }) expect(of.call(MyType, 'abc')).toEqual(expected) }) }) diff --git a/test/npm/array.prototype.every.test.mts b/test/npm/array.prototype.every.test.mts index 66824c3b..80a4ae1c 100644 --- a/test/npm/array.prototype.every.test.mts +++ b/test/npm/array.prototype.every.test.mts @@ -89,15 +89,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - every([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('true thunk callback yields true', () => { expect(every([], trueThunk)).toBe(true) diff --git a/test/npm/array.prototype.filter.test.mts b/test/npm/array.prototype.filter.test.mts index 236fbfa7..fdf22350 100644 --- a/test/npm/array.prototype.filter.test.mts +++ b/test/npm/array.prototype.filter.test.mts @@ -170,15 +170,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - filter([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('returns a new empty array', () => { const arr: any[] = [] diff --git a/test/npm/array.prototype.findlast.test.mts b/test/npm/array.prototype.findlast.test.mts index 60f5da68..69f4b868 100644 --- a/test/npm/array.prototype.findlast.test.mts +++ b/test/npm/array.prototype.findlast.test.mts @@ -111,15 +111,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - findLast([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('true thunk callback yields undefined', () => { expect(findLast([], trueThunk)).toBe(undefined) diff --git a/test/npm/array.prototype.findlastindex.test.mts b/test/npm/array.prototype.findlastindex.test.mts index aeb9f08c..b6a43c28 100644 --- a/test/npm/array.prototype.findlastindex.test.mts +++ b/test/npm/array.prototype.findlastindex.test.mts @@ -111,15 +111,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - findLastIndex([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('true thunk callback yields -1', () => { expect(findLastIndex([], trueThunk)).toBe(-1) diff --git a/test/npm/array.prototype.flatmap.test.mts b/test/npm/array.prototype.flatmap.test.mts new file mode 100644 index 00000000..4e404ad7 --- /dev/null +++ b/test/npm/array.prototype.flatmap.test.mts @@ -0,0 +1,81 @@ +/** + * @fileoverview Tests for array.prototype.flatmap NPM package override. + * Ported 1:1 from upstream v1.3.3 (7ebde137): + * https://github.com/es-shims/Array.prototype.flatMap/blob/7ebde137246788a04036a73f1a6ffed5b600f22e/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: flatMap, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('callback function', () => { + it.each([[], {}, true, false, 42, 'foo', /a/g, null])( + 'throws for non-function %s', + nonFunction => { + expect(() => flatMap([], nonFunction)).toThrow(TypeError) + }, + ) + }) + + describe('flatMaps', () => { + it('flattens and maps to tuples of item/index', () => { + const mapped = flatMap([1, [2], [3, 4]], (x: unknown, i: number) => [ + x, + i, + ]) + const expected = [1, 0, [2], 1, [3, 4], 2] + expect(mapped).toEqual(expected) + expect(mapped.length).toBe(expected.length) + }) + + it('thisArg works as expected', () => { + const context = {} + let actual: unknown + flatMap( + [1], + function (this: unknown) { + // eslint-disable-next-line @typescript-eslint/no-this-alias + actual = this + }, + context, + ) + expect(actual).toBe(context) + }) + }) + + describe('sparse arrays', () => { + it('an array hole is treated the same as an empty array', () => { + const identity = (x: unknown) => x + // eslint-disable-next-line no-sparse-arrays + expect(flatMap([, [1]], identity)).toEqual(flatMap([[], [1]], identity)) + }) + }) + + describe('test262: staging test from v8', () => { + it('handles array growth during callback', () => { + const arr1 = [0, 1, 2, 3] + const f = (e: number) => { + arr1[4] = 42 + return e + } + expect(flatMap(arr1, f)).toEqual([0, 1, 2, 3]) + }) + + it('handles array shrink during callback', () => { + const arr2 = [0, 1, 2, 3] + const g = (e: number) => { + arr2.length = 3 + return e + } + expect(flatMap(arr2, g)).toEqual([0, 1, 2]) + }) + }) +}) diff --git a/test/npm/array.prototype.foreach.test.mts b/test/npm/array.prototype.foreach.test.mts index a1bac7cc..7d6bf972 100644 --- a/test/npm/array.prototype.foreach.test.mts +++ b/test/npm/array.prototype.foreach.test.mts @@ -126,15 +126,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - forEach([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('returns undefined', () => { const arr: any[] = [] diff --git a/test/npm/array.prototype.map.test.mts b/test/npm/array.prototype.map.test.mts index bd9eaa25..7bfd5619 100644 --- a/test/npm/array.prototype.map.test.mts +++ b/test/npm/array.prototype.map.test.mts @@ -148,15 +148,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ]) }) - it('sets the right context when given none (sloppy mode)', () => { - let context: any - map([1], function (this: any) { - // eslint-disable-next-line @typescript-eslint/no-this-alias - context = this - }) - expect(context).toBe(globalThis) - }) - describe('empty array', () => { it('returns a new empty array', () => { const arr: any[] = [] diff --git a/test/npm/array.prototype.reduce.test.mts b/test/npm/array.prototype.reduce.test.mts index 7c762291..a059a6e5 100644 --- a/test/npm/array.prototype.reduce.test.mts +++ b/test/npm/array.prototype.reduce.test.mts @@ -57,7 +57,7 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { expect(value).toBe(expectedValue) expect(key).toBe(0) expect(list).toBe(arr) - expect(this).toBe(globalThis) + expect(this).toBe(undefined) return expectedResult }, initialValue, diff --git a/test/npm/arraybuffer.prototype.slice.test.mts b/test/npm/arraybuffer.prototype.slice.test.mts new file mode 100644 index 00000000..4b3a7d3f --- /dev/null +++ b/test/npm/arraybuffer.prototype.slice.test.mts @@ -0,0 +1,79 @@ +/** + * @fileoverview Tests for arraybuffer.prototype.slice NPM package override. + * Ported 1:1 from upstream v1.0.4 (d8ac7211): + * https://github.com/es-shims/ArrayBuffer.prototype.slice/blob/d8ac72119e74c40234e6da003763fa564d49ac0e/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: slice, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws for non-ArrayBuffer values', () => { + const nonABs = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + {}, + [], + function () {}, + /a/g, + ] + for (const nonAB of nonABs) { + expect(() => slice(nonAB)).toThrow(TypeError) + } + }) + + describe('ArrayBuffers', { skip: typeof ArrayBuffer === 'undefined' }, () => { + it('slices an empty ArrayBuffer', () => { + const ab = new ArrayBuffer(0) + const nb = slice(ab) + expect(nb).not.toBe(ab) + }) + + it('slices with start offset', () => { + const ab2 = new ArrayBuffer(8) + expect(ab2.byteLength).toBe(8) + const nbLen = slice(ab2, 4) + expect(nbLen.byteLength).toBe(4) + }) + + it('slice produces an independent copy', () => { + const one = new ArrayBuffer(1) + const arr = new Uint8Array(one) + arr[0] = 123 + + const two = slice(one) + const arr2 = new Uint8Array(two) + arr2[0] = 234 + + expect(arr).toEqual(new Uint8Array([123])) + expect(arr2).toEqual(new Uint8Array([234])) + }) + }) + + describe( + 'SharedArrayBuffers', + { skip: typeof SharedArrayBuffer === 'undefined' }, + () => { + it('throws for SharedArrayBuffer', () => { + const sab = new SharedArrayBuffer(0) + expect(() => slice(sab)).toThrow(TypeError) + }) + }, + ) +}) diff --git a/test/npm/es-aggregate-error.test.mts b/test/npm/es-aggregate-error.test.mts new file mode 100644 index 00000000..3d78ff35 --- /dev/null +++ b/test/npm/es-aggregate-error.test.mts @@ -0,0 +1,92 @@ +/** + * @fileoverview Tests for es-aggregate-error NPM package override. + * Ported 1:1 from upstream v1.0.14 (9a60200e): + * https://github.com/es-shims/AggregateError/blob/9a60200ed1a2128f48ef2d00e5a07d7c1a09c49c/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: AggregateError, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('constructor', () => { + it('is a function', () => { + expect(typeof AggregateError).toBe('function') + }) + + it('has a length of 2', () => { + expect(AggregateError.length).toBe(2) + }) + + it('prototype is not writable, not enumerable, not configurable', () => { + const desc = Object.getOwnPropertyDescriptor(AggregateError, 'prototype') + expect(desc).toEqual({ + configurable: false, + enumerable: false, + value: AggregateError.prototype, + writable: false, + }) + }) + }) + + it('prototype.message is empty string', () => { + expect(AggregateError.prototype.message).toBe('') + }) + + describe('non-iterable errors', () => { + it.each([ + undefined, + null, + true, + false, + 42, + NaN, + 0, + -0, + Infinity, + function () {}, + {}, + ])('throws TypeError for non-iterable %s', nonIterable => { + expect(() => new AggregateError(nonIterable)).toThrow(TypeError) + }) + }) + + describe('instance', () => { + it('is instanceof AggregateError and Error', () => { + const one = new TypeError('one!') + const two = new EvalError('two!') + const errors = [one, two] + const message = 'i am an aggregate error' + const error = new AggregateError(errors, message) + + expect(error instanceof AggregateError).toBe(true) + expect(error instanceof Error).toBe(true) + expect(error.message).toBe(message) + expect(error.errors).not.toBe(errors) + expect(error.errors).toEqual(errors) + }) + }) + + describe('as a function', () => { + it('works when called without new', () => { + const one = new TypeError('one!') + const two = new EvalError('two!') + const errors = [one, two] + const message = 'i am an aggregate error' + const error = AggregateError(errors, message) + + expect(error instanceof AggregateError).toBe(true) + expect(error instanceof Error).toBe(true) + expect(error.message).toBe(message) + expect(error.errors).not.toBe(errors) + expect(error.errors).toEqual(errors) + }) + }) +}) diff --git a/test/npm/es-iterator-helpers.test.mts b/test/npm/es-iterator-helpers.test.mts new file mode 100644 index 00000000..0afce497 --- /dev/null +++ b/test/npm/es-iterator-helpers.test.mts @@ -0,0 +1,215 @@ +/** + * @fileoverview Tests for es-iterator-helpers NPM package override. + * Ported 1:1 from upstream v1.3.2 (1a9241c3): + * https://github.com/es-shims/iterator-helpers/blob/1a9241c33779dce25110474feedb2c4a1b15c7ff/test/Iterator.js + */ + +import path from 'node:path' + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: iteratorHelpers, + pkgPath, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const loadSub = (subPath: string) => { + if (skip) return undefined + try { + return require(path.join(pkgPath, subPath)) + } catch { + return undefined + } +} + +const iteratorFrom = loadSub('Iterator.from') +const iteratorMap = loadSub('Iterator.prototype.map') +const iteratorFilter = loadSub('Iterator.prototype.filter') +const iteratorTake = loadSub('Iterator.prototype.take') +const iteratorDrop = loadSub('Iterator.prototype.drop') +const iteratorToArray = loadSub('Iterator.prototype.toArray') +const iteratorForEach = loadSub('Iterator.prototype.forEach') +const iteratorSome = loadSub('Iterator.prototype.some') +const iteratorEvery = loadSub('Iterator.prototype.every') +const iteratorFind = loadSub('Iterator.prototype.find') +const iteratorReduce = loadSub('Iterator.prototype.reduce') +const iteratorFlatMap = loadSub('Iterator.prototype.flatMap') + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('exports expected methods', () => { + expect(Array.isArray(iteratorHelpers)).toBe(true) + }) + + describe('Iterator.from', () => { + it('is a function', { skip: !iteratorFrom }, () => { + expect(typeof iteratorFrom).toBe('function') + }) + + it( + 'wraps an array iterator', + { skip: !iteratorFrom || typeof Symbol === 'undefined' }, + () => { + const iter = iteratorFrom([1, 2, 3]) + expect(iter.next()).toEqual({ value: 1, done: false }) + expect(iter.next()).toEqual({ value: 2, done: false }) + expect(iter.next()).toEqual({ value: 3, done: false }) + expect(iter.next().done).toBe(true) + }, + ) + + it( + 'wraps a string iterator', + { skip: !iteratorFrom || typeof Symbol === 'undefined' }, + () => { + const iter = iteratorFrom('abc') + expect(iter.next()).toEqual({ value: 'a', done: false }) + expect(iter.next()).toEqual({ value: 'b', done: false }) + expect(iter.next()).toEqual({ value: 'c', done: false }) + expect(iter.next().done).toBe(true) + }, + ) + }) + + describe('Iterator.prototype.map', () => { + it('maps values', { skip: !iteratorMap }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + const mapped = iteratorMap(iter, (x: number) => x * 2) + expect(mapped.next()).toEqual({ value: 2, done: false }) + expect(mapped.next()).toEqual({ value: 4, done: false }) + expect(mapped.next()).toEqual({ value: 6, done: false }) + expect(mapped.next().done).toBe(true) + }) + }) + + describe('Iterator.prototype.filter', () => { + it('filters values', { skip: !iteratorFilter }, () => { + const arr = [1, 2, 3, 4, 5] + const iter = arr[Symbol.iterator]() + const filtered = iteratorFilter(iter, (x: number) => x % 2 === 0) + expect(filtered.next()).toEqual({ value: 2, done: false }) + expect(filtered.next()).toEqual({ value: 4, done: false }) + expect(filtered.next().done).toBe(true) + }) + }) + + describe('Iterator.prototype.take', () => { + it('takes first n values', { skip: !iteratorTake }, () => { + const arr = [1, 2, 3, 4, 5] + const iter = arr[Symbol.iterator]() + const taken = iteratorTake(iter, 3) + expect(taken.next()).toEqual({ value: 1, done: false }) + expect(taken.next()).toEqual({ value: 2, done: false }) + expect(taken.next()).toEqual({ value: 3, done: false }) + expect(taken.next().done).toBe(true) + }) + }) + + describe('Iterator.prototype.drop', () => { + it('drops first n values', { skip: !iteratorDrop }, () => { + const arr = [1, 2, 3, 4, 5] + const iter = arr[Symbol.iterator]() + const dropped = iteratorDrop(iter, 3) + expect(dropped.next()).toEqual({ value: 4, done: false }) + expect(dropped.next()).toEqual({ value: 5, done: false }) + expect(dropped.next().done).toBe(true) + }) + }) + + describe('Iterator.prototype.toArray', () => { + it('collects values into an array', { skip: !iteratorToArray }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + const result = iteratorToArray(iter) + expect(result).toEqual([1, 2, 3]) + }) + }) + + describe('Iterator.prototype.forEach', () => { + it('calls callback for each value', { skip: !iteratorForEach }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + const results: number[] = [] + iteratorForEach(iter, (x: number) => { + results.push(x) + }) + expect(results).toEqual([1, 2, 3]) + }) + }) + + describe('Iterator.prototype.some', () => { + it('returns true when predicate matches', { skip: !iteratorSome }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + expect(iteratorSome(iter, (x: number) => x === 2)).toBe(true) + }) + + it( + 'returns false when predicate never matches', + { skip: !iteratorSome }, + () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + expect(iteratorSome(iter, (x: number) => x === 4)).toBe(false) + }, + ) + }) + + describe('Iterator.prototype.every', () => { + it('returns true when all match', { skip: !iteratorEvery }, () => { + const arr = [2, 4, 6] + const iter = arr[Symbol.iterator]() + expect(iteratorEvery(iter, (x: number) => x % 2 === 0)).toBe(true) + }) + + it( + 'returns false when one does not match', + { skip: !iteratorEvery }, + () => { + const arr = [2, 3, 6] + const iter = arr[Symbol.iterator]() + expect(iteratorEvery(iter, (x: number) => x % 2 === 0)).toBe(false) + }, + ) + }) + + describe('Iterator.prototype.find', () => { + it('finds a matching value', { skip: !iteratorFind }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + expect(iteratorFind(iter, (x: number) => x === 2)).toBe(2) + }) + + it('returns undefined when not found', { skip: !iteratorFind }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + expect(iteratorFind(iter, (x: number) => x === 4)).toBeUndefined() + }) + }) + + describe('Iterator.prototype.reduce', () => { + it('reduces values with accumulator', { skip: !iteratorReduce }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + const sum = iteratorReduce(iter, (_acc: number, x: number) => _acc + x, 0) + expect(sum).toBe(6) + }) + }) + + describe('Iterator.prototype.flatMap', () => { + it('flat maps values', { skip: !iteratorFlatMap }, () => { + const arr = [1, 2, 3] + const iter = arr[Symbol.iterator]() + const flatMapped = iteratorFlatMap(iter, (x: number) => [x, x * 10]) + const result = iteratorToArray + ? iteratorToArray(flatMapped) + : [...flatMapped] + expect(result).toEqual([1, 10, 2, 20, 3, 30]) + }) + }) +}) diff --git a/test/npm/es6-symbol.test.mts b/test/npm/es6-symbol.test.mts new file mode 100644 index 00000000..e0f28d3e --- /dev/null +++ b/test/npm/es6-symbol.test.mts @@ -0,0 +1,32 @@ +/** + * @fileoverview Tests for es6-symbol NPM package override. + * Ported 1:1 from upstream v3.1.4 (c57f8d88): + * https://github.com/medikoo/es6-symbol/blob/c57f8d88070cec913ff4e3e0ed5192b2330373b2/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: SymbolPolyfill, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('creates a symbol', () => { + const symbol = SymbolPolyfill('test') + expect(symbol).toBeDefined() + expect(typeof symbol).toBe('symbol') + }) + + it('symbol property is not accessible by name', () => { + const symbol = SymbolPolyfill('test') + const obj: Record = {} + obj[symbol] = 'foo' + expect((obj as any).test).toBeUndefined() + expect(obj[symbol]).toBe('foo') + }) +}) diff --git a/test/npm/function-bind.test.mts b/test/npm/function-bind.test.mts index 22d2b8a2..a200df2c 100644 --- a/test/npm/function-bind.test.mts +++ b/test/npm/function-bind.test.mts @@ -79,7 +79,7 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { }, null), } const context = namespace.func(1, 2, 3) - expect(context).toBe(getCurrentContext.call(undefined)) + expect(context).toBe(getCurrentContext.call(null)) expect(args).toEqual([1, 2, 3]) }) @@ -98,7 +98,7 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ), } const context = namespace.func(4, 5, 6) - expect(context).toBe(getCurrentContext.call(undefined)) + expect(context).toBe(getCurrentContext.call(null)) expect(args).toEqual([1, 2, 3, 4, 5, 6]) }) diff --git a/test/npm/functions-have-names.test.mts b/test/npm/functions-have-names.test.mts new file mode 100644 index 00000000..a3cffdb5 --- /dev/null +++ b/test/npm/functions-have-names.test.mts @@ -0,0 +1,82 @@ +/** + * @fileoverview Tests for functions-have-names NPM package override. + * Ported 1:1 from upstream v1.2.3 (64b47a22): + * https://github.com/inspect-js/functions-have-names/blob/64b47a22365205afc910782e99fc4b554d937171/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: hasNames, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('named functions', () => { + it('is a function', () => { + expect(typeof hasNames).toBe('function') + }) + + it('returns expected result', () => { + function f() {} + const g = function h() {} + expect(hasNames()).toBe(f.name === 'f' && g.name === 'h') + }) + }) + + describe('functionsHaveConfigurableNames', () => { + it('is a function', () => { + expect(typeof hasNames.functionsHaveConfigurableNames).toBe('function') + }) + + it('returns a boolean', () => { + expect(typeof hasNames.functionsHaveConfigurableNames()).toBe('boolean') + }) + + it('returns correct result based on configurability', () => { + if (hasNames()) { + const fn = function f() {} + const oDP = Object.defineProperty + if (oDP) { + try { + oDP(fn, 'name', { configurable: true, value: 'foo' }) + } catch (_e) { + // ignore + } + if (fn.name === 'f') { + expect(hasNames.functionsHaveConfigurableNames()).toBe(false) + } else if (fn.name === 'foo') { + expect(hasNames.functionsHaveConfigurableNames()).toBe(true) + } + } else { + expect(hasNames.functionsHaveConfigurableNames()).toBe(false) + } + } else { + expect(hasNames.functionsHaveConfigurableNames()).toBe(false) + } + }) + }) + + describe('boundFunctionsHaveNames', () => { + it('is a function', () => { + expect(typeof hasNames.boundFunctionsHaveNames).toBe('function') + }) + + it('returns correct result', () => { + const fn = function f() {} + if (typeof fn.bind !== 'function') { + expect(hasNames.boundFunctionsHaveNames()).toBe(false) + } else if (hasNames()) { + expect(hasNames.boundFunctionsHaveNames()).toBe( + fn.bind(undefined).name !== '', + ) + } else { + expect(hasNames.boundFunctionsHaveNames()).toBe(false) + } + }) + }) +}) diff --git a/test/npm/globalthis.test.mts b/test/npm/globalthis.test.mts new file mode 100644 index 00000000..4658c5c2 --- /dev/null +++ b/test/npm/globalthis.test.mts @@ -0,0 +1,75 @@ +/** + * @fileoverview Tests for globalthis NPM package override. + * Ported 1:1 from upstream v1.0.4 (1776bde5): + * https://github.com/ljharb/System.global/blob/1776bde58ddfc1e3de7d49c859acdce57a5b21a0/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: getGlobal, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const theGlobal = skip ? undefined : getGlobal() + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function that returns an object', () => { + expect(typeof getGlobal).toBe('function') + expect(typeof theGlobal).toBe('object') + }) + + describe('built-in globals', () => { + it('Math is on the global', () => { + expect(theGlobal.Math).toBe(Math) + }) + + it('JSON is on the global', () => { + expect(theGlobal.JSON).toBe(JSON) + }) + + it('String is on the global', () => { + expect(theGlobal.String).toBe(String) + }) + + it('Array is on the global', () => { + expect(theGlobal.Array).toBe(Array) + }) + + it('Number is on the global', () => { + expect(theGlobal.Number).toBe(Number) + }) + + it('Boolean is on the global', () => { + expect(theGlobal.Boolean).toBe(Boolean) + }) + + it('Object is on the global', () => { + expect(theGlobal.Object).toBe(Object) + }) + + it('Function is on the global', () => { + expect(theGlobal.Function).toBe(Function) + }) + + it('Date is on the global', () => { + expect(theGlobal.Date).toBe(Date) + }) + + it('RegExp is on the global', () => { + expect(theGlobal.RegExp).toBe(RegExp) + }) + + it( + 'Symbol is on the global', + { skip: typeof Symbol === 'undefined' }, + () => { + expect(theGlobal.Symbol).toBe(Symbol) + }, + ) + }) +}) diff --git a/test/npm/gopd.test.mts b/test/npm/gopd.test.mts new file mode 100644 index 00000000..cc919bc0 --- /dev/null +++ b/test/npm/gopd.test.mts @@ -0,0 +1,43 @@ +/** + * @fileoverview Tests for gopd NPM package override. + * Ported 1:1 from upstream v1.2.0 (c204ca2d): + * https://github.com/ljharb/gopd/blob/c204ca2d2aced0ba4e336007b46a9cbef3c23311/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: gOPD, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('supported', { skip: !gOPD }, () => { + it('is a function', () => { + expect(typeof gOPD).toBe('function') + }) + + it('returns expected descriptor', () => { + const obj = { x: 1 } + expect('x' in obj).toBe(true) + + const desc = gOPD(obj, 'x') + expect(desc).toEqual({ + configurable: true, + enumerable: true, + value: 1, + writable: true, + }) + }) + }) + + describe('not supported', { skip: !!gOPD }, () => { + it('is falsy', () => { + expect(gOPD).toBeFalsy() + }) + }) +}) diff --git a/test/npm/harmony-reflect.test.mts b/test/npm/harmony-reflect.test.mts index f9f28381..a6d4d506 100644 --- a/test/npm/harmony-reflect.test.mts +++ b/test/npm/harmony-reflect.test.mts @@ -172,7 +172,6 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { }) it('should correctly implement construct', () => { - expect(harmonyReflect.construct((x: number) => x, [1])).not.toBe(1) expect( harmonyReflect.construct( function (this: { x: number }, x: number) { diff --git a/test/npm/has-property-descriptors.test.mts b/test/npm/has-property-descriptors.test.mts new file mode 100644 index 00000000..b9e0828d --- /dev/null +++ b/test/npm/has-property-descriptors.test.mts @@ -0,0 +1,68 @@ +/** + * @fileoverview Tests for has-property-descriptors NPM package override. + * Ported 1:1 from upstream v1.0.2 (4ac370bb): + * https://github.com/inspect-js/has-property-descriptors/blob/4ac370bba561e51dacf87cd43b69c26dd44cd173/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: hasPropertyDescriptors, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof hasPropertyDescriptors).toBe('function') + }) + + it('hasArrayLengthDefineBug is a function', () => { + expect(typeof hasPropertyDescriptors.hasArrayLengthDefineBug).toBe( + 'function', + ) + }) + + const yes = hasPropertyDescriptors() + + describe('property descriptors', { skip: !yes }, () => { + it('has expected property descriptor', () => { + const sentinel = {} + const o = { a: sentinel } + + expect(Object.getOwnPropertyDescriptor(o, 'a')).toEqual({ + configurable: true, + enumerable: true, + value: sentinel, + writable: true, + }) + + Object.defineProperty(o, 'a', { enumerable: false, writable: false }) + + expect(Object.getOwnPropertyDescriptor(o, 'a')).toEqual({ + configurable: true, + enumerable: false, + value: sentinel, + writable: false, + }) + }) + }) + + describe( + 'defining array lengths', + { skip: !yes || hasPropertyDescriptors.hasArrayLengthDefineBug() }, + () => { + it('can define array length', () => { + // eslint-disable-next-line no-sparse-arrays + const arr = [1, , 3] + expect(arr.length).toBe(3) + + Object.defineProperty(arr, 'length', { value: 5 }) + expect(arr.length).toBe(5) + }) + }, + ) +}) diff --git a/test/npm/has-proto.test.mts b/test/npm/has-proto.test.mts new file mode 100644 index 00000000..796245ae --- /dev/null +++ b/test/npm/has-proto.test.mts @@ -0,0 +1,33 @@ +/** + * @fileoverview Tests for has-proto NPM package override. + * Ported 1:1 from upstream v1.2.0 (8f9ec131): + * https://github.com/inspect-js/has-proto/blob/8f9ec1310fbedee155bd1022941a3fd80ef835f6/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: hasProto, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('returns a boolean', () => { + const result = hasProto() + expect(typeof result).toBe('boolean') + }) + + it('correctly detects __proto__ support', () => { + const result = hasProto() + const obj = { __proto__: null } + if (result) { + expect('toString' in obj).toBe(false) + } else { + expect('toString' in obj).toBe(true) + } + }) +}) diff --git a/test/npm/has-symbols.test.mts b/test/npm/has-symbols.test.mts new file mode 100644 index 00000000..32bff526 --- /dev/null +++ b/test/npm/has-symbols.test.mts @@ -0,0 +1,75 @@ +/** + * @fileoverview Tests for has-symbols NPM package override. + * Ported 1:1 from upstream v1.1.0 (7aefe479): + * https://github.com/inspect-js/has-symbols/blob/7aefe479e315c27d742a2a329207d8eb7d3a5cec/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: hasSymbols, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof hasSymbols).toBe('function') + }) + + it('returns a boolean', () => { + expect(typeof hasSymbols()).toBe('boolean') + }) + + describe('Symbols are supported', { skip: !hasSymbols() }, () => { + it('Symbol is a function', () => { + expect(typeof Symbol).toBe('function') + }) + + it('two symbols are not equal', () => { + expect(Symbol()).not.toBe(Symbol()) + }) + + it('Symbol#toString is a function', () => { + expect(typeof Symbol.prototype.toString).toBe('function') + }) + + it('Object.getOwnPropertySymbols is a function', () => { + expect(typeof Object.getOwnPropertySymbols).toBe('function') + }) + + it('symbol property is not enumerable', () => { + const obj: Record = {} + const sym = Symbol('test') + obj[sym] = 42 + + for (const _key in obj) { + expect.fail('symbol property key was found in for..in of object') + } + + expect(Object.keys(obj)).toEqual([]) + expect(Object.getOwnPropertyNames(obj)).toEqual([]) + expect(Object.getOwnPropertySymbols(obj)).toEqual([sym]) + expect(Object.prototype.propertyIsEnumerable.call(obj, sym)).toBe(true) + expect(Object.getOwnPropertyDescriptor(obj, sym)).toEqual({ + configurable: true, + enumerable: true, + value: 42, + writable: true, + }) + }) + }) + + describe('Symbols are not supported', { skip: hasSymbols() }, () => { + it('global Symbol is undefined', () => { + expect(typeof Symbol).toBe('undefined') + }) + + it('Object.getOwnPropertySymbols does not exist', () => { + expect(typeof Object.getOwnPropertySymbols).toBe('undefined') + }) + }) +}) diff --git a/test/npm/has.test.mts b/test/npm/has.test.mts new file mode 100644 index 00000000..d97a753f --- /dev/null +++ b/test/npm/has.test.mts @@ -0,0 +1,34 @@ +/** + * @fileoverview Tests for has NPM package override. + * Ported 1:1 from upstream v1.0.4 (50e19324): + * https://github.com/tarruda/has/blob/50e19324b8aeb19b4534d5f4e38bba1405463bc1/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: has, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('object literal does not have own property "hasOwnProperty"', () => { + expect(has({}, 'hasOwnProperty')).toBe(false) + }) + + it('Object.prototype has own property "hasOwnProperty"', () => { + expect(has(Object.prototype, 'hasOwnProperty')).toBe(true) + }) + + it('calling has on null throws TypeError', () => { + expect(() => has(null, 'throws')).toThrow(TypeError) + }) + + it('calling has on undefined throws TypeError', () => { + expect(() => has(undefined, 'throws')).toThrow(TypeError) + }) +}) diff --git a/test/npm/internal-slot.test.mts b/test/npm/internal-slot.test.mts new file mode 100644 index 00000000..029ea7b2 --- /dev/null +++ b/test/npm/internal-slot.test.mts @@ -0,0 +1,202 @@ +/** + * @fileoverview Tests for internal-slot NPM package override. + * Ported 1:1 from upstream v1.1.0 (705000a7): + * https://github.com/ljharb/internal-slot/blob/705000a750605f9c01af424de2ededc20403f87d/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: SLOT, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('assert', () => { + it('throws for primitives', () => { + for (const primitive of [ + null, + undefined, + true, + false, + 'foo', + '', + 42, + 0, + ]) { + expect(() => SLOT.assert(primitive, '')).toThrow(TypeError) + } + }) + + it('throws for non-string slot names', () => { + for (const nonString of [ + null, + undefined, + true, + false, + 42, + 0, + {}, + [], + function () {}, + /a/g, + ]) { + expect(() => SLOT.assert({}, nonString)).toThrow(TypeError) + } + }) + + it('throws for nonexistent slot', () => { + expect(() => SLOT.assert({}, '[[whatever]]')).toThrow(TypeError) + }) + + it('does not throw for existent slot', () => { + const o = {} + SLOT.set(o, 'x') + expect(() => SLOT.assert(o, 'x')).not.toThrow() + }) + + it('thing with a slot throws on a nonexistent slot', () => { + const o = {} + SLOT.set(o, 'x') + expect(() => SLOT.assert(o, 'y')).toThrow(TypeError) + }) + }) + + describe('has', () => { + it('throws for primitives', () => { + for (const primitive of [ + null, + undefined, + true, + false, + 'foo', + '', + 42, + 0, + ]) { + expect(() => SLOT.has(primitive, '')).toThrow(TypeError) + } + }) + + it('throws for non-string slot names', () => { + for (const nonString of [ + null, + undefined, + true, + false, + 42, + 0, + {}, + [], + function () {}, + /a/g, + ]) { + expect(() => SLOT.has({}, nonString)).toThrow(TypeError) + } + }) + + it('returns false for nonexistent slot', () => { + expect(SLOT.has({}, '[[nonexistent]]')).toBe(false) + }) + + it('returns true for existent slot', () => { + const o = {} + SLOT.set(o, 'foo') + expect(SLOT.has(o, 'foo')).toBe(true) + }) + }) + + describe('get', () => { + it('throws for primitives', () => { + for (const primitive of [ + null, + undefined, + true, + false, + 'foo', + '', + 42, + 0, + ]) { + expect(() => SLOT.get(primitive, '')).toThrow(TypeError) + } + }) + + it('throws for non-string slot names', () => { + for (const nonString of [ + null, + undefined, + true, + false, + 42, + 0, + {}, + [], + function () {}, + /a/g, + ]) { + expect(() => SLOT.get({}, nonString)).toThrow(TypeError) + } + }) + + it('returns undefined for nonexistent slot', () => { + expect(SLOT.get({}, 'nonexistent')).toBeUndefined() + }) + + it('retrieves value set by "set"', () => { + const o = {} + const v = {} + SLOT.set(o, 'f', v) + expect(SLOT.get(o, 'f')).toBe(v) + }) + }) + + describe('set', () => { + it('throws for primitives', () => { + for (const primitive of [ + null, + undefined, + true, + false, + 'foo', + '', + 42, + 0, + ]) { + expect(() => SLOT.set(primitive, '')).toThrow(TypeError) + } + }) + + it('throws for non-string slot names', () => { + for (const nonString of [ + null, + undefined, + true, + false, + 42, + 0, + {}, + [], + function () {}, + /a/g, + ]) { + expect(() => SLOT.set({}, nonString)).toThrow(TypeError) + } + }) + + it('sets and updates slot values', () => { + const o = function () {} + expect(SLOT.get(o, 'f')).toBeUndefined() + + SLOT.set(o, 'f', 42) + expect(SLOT.get(o, 'f')).toBe(42) + + SLOT.set(o, 'f', Infinity) + expect(SLOT.get(o, 'f')).toBe(Infinity) + }) + }) +}) diff --git a/test/npm/is-arguments.test.mts b/test/npm/is-arguments.test.mts new file mode 100644 index 00000000..026686c4 --- /dev/null +++ b/test/npm/is-arguments.test.mts @@ -0,0 +1,84 @@ +/** + * @fileoverview Tests for is-arguments NPM package override. + * Ported 1:1 from upstream v1.2.0 (8d6e11a8): + * https://github.com/inspect-js/is-arguments/blob/8d6e11a8f6b2616a70ada0da45c3ee860c633af6/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isArguments, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasToStringTag = + typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('primitives', () => { + it('array is not arguments', () => { + expect(isArguments([])).toBe(false) + }) + + it('object is not arguments', () => { + expect(isArguments({})).toBe(false) + }) + + it('empty string is not arguments', () => { + expect(isArguments('')).toBe(false) + }) + + it('string is not arguments', () => { + expect(isArguments('foo')).toBe(false) + }) + + it('naive array-like is not arguments', () => { + expect(isArguments({ length: 2 })).toBe(false) + }) + }) + + describe('arguments object', () => { + it('arguments is arguments', () => { + ;(function () { + expect(isArguments(arguments)).toBe(true) + })() + }) + + it('sliced arguments is not arguments', () => { + ;(function () { + expect(isArguments(Array.prototype.slice.call(arguments))).toBe(false) + })() + }) + }) + + describe('old-style arguments object', () => { + it('old-style arguments is arguments', () => { + const isLegacyArguments = isArguments.isLegacyArguments || isArguments + const fakeOldArguments = { + callee: function () {}, + length: 3, + } + expect(isLegacyArguments(fakeOldArguments)).toBe(true) + }) + }) + + describe('Symbol.toStringTag', { skip: !hasToStringTag }, () => { + it('object with faked toStringTag is not arguments', () => { + const obj: Record = {} + obj[Symbol.toStringTag] = 'Arguments' + expect(isArguments(obj)).toBe(false) + }) + + it('real arguments with faked toStringTag is not arguments', () => { + const args: any = (function () { + return arguments + })() + args[Symbol.toStringTag] = 'Arguments' + expect(isArguments(args)).toBe(false) + }) + }) +}) diff --git a/test/npm/is-array-buffer.test.mts b/test/npm/is-array-buffer.test.mts new file mode 100644 index 00000000..f1b89175 --- /dev/null +++ b/test/npm/is-array-buffer.test.mts @@ -0,0 +1,99 @@ +/** + * @fileoverview Tests for is-array-buffer NPM package override. + * Ported 1:1 from upstream v3.0.5 (9a91db98): + * https://github.com/inspect-js/is-array-buffer/blob/9a91db98411737952175ef8d6ede3301e570fbc1/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isArrayBuffer, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof isArrayBuffer).toBe('function') + }) + + it('returns false for non-ArrayBuffer values', () => { + const nonABs: unknown[] = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + {}, + [], + function () {}, + /a/g, + ] + if (typeof SharedArrayBuffer === 'function') { + nonABs.push(new SharedArrayBuffer(0)) + } + for (const nonAB of nonABs) { + expect(isArrayBuffer(nonAB)).toBe(false) + } + }) + + describe( + 'actual ArrayBuffer instances', + { skip: typeof ArrayBuffer === 'undefined' }, + () => { + it('empty ArrayBuffer is an ArrayBuffer', () => { + expect(isArrayBuffer(new ArrayBuffer(0))).toBe(true) + }) + + it('ArrayBuffer(42) is an ArrayBuffer', () => { + const ab42 = new ArrayBuffer(42) + expect(isArrayBuffer(ab42)).toBe(true) + }) + + it('DataView is not an ArrayBuffer', () => { + const ab42 = new ArrayBuffer(42) + const dv = new DataView(ab42) + expect(isArrayBuffer(dv)).toBe(false) + }) + }, + ) + + describe('Typed Arrays', () => { + const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', + ] + + for (const name of typedArrayNames) { + const TypedArray = (globalThis as any)[name] + if (typeof TypedArray === 'function') { + it(`${name} buffer is an ArrayBuffer`, () => { + const ta = new TypedArray(0) + expect(isArrayBuffer(ta.buffer)).toBe(true) + }) + + it(`${name} instance is not an ArrayBuffer`, () => { + const ta = new TypedArray(0) + expect(isArrayBuffer(ta)).toBe(false) + }) + } + } + }) +}) diff --git a/test/npm/is-bigint.test.mts b/test/npm/is-bigint.test.mts new file mode 100644 index 00000000..0324152f --- /dev/null +++ b/test/npm/is-bigint.test.mts @@ -0,0 +1,107 @@ +/** + * @fileoverview Tests for is-bigint NPM package override. + * Ported 1:1 from upstream v1.1.0 (053a24a4): + * https://github.com/inspect-js/is-bigint/blob/053a24a40506e5f90e12e265583f65575d4f5703/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isBigInt, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasBigInts = typeof BigInt === 'function' +const hasToStringTag = + typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-BigInt values', () => { + it.each([ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + {}, + [], + /a/g, + new Date(), + function () {}, + ])('returns false for %s', value => { + expect(isBigInt(value)).toBe(false) + }) + + it('returns false for wrapped booleans', () => { + expect(isBigInt(Object(true))).toBe(false) + expect(isBigInt(Object(false))).toBe(false) + }) + }) + + describe('faked BigInt values', () => { + it( + 'object with valueOf returning a BigInt is not a BigInt', + { skip: !hasBigInts }, + () => { + const fakeBigInt = { + valueOf: function () { + return BigInt(42) + }, + } + expect(isBigInt(fakeBigInt)).toBe(false) + }, + ) + + it( + 'faked @@toStringTag with valueOf returning a BigInt is not a BigInt', + { skip: !hasBigInts || !hasToStringTag }, + () => { + const fakeBigInt: Record = { + valueOf: function () { + return BigInt(42) + }, + } + fakeBigInt[Symbol.toStringTag] = 'BigInt' + expect(isBigInt(fakeBigInt)).toBe(false) + + const notSoFakeBigInt: Record = { + valueOf: function () { + return 42 + }, + } + notSoFakeBigInt[Symbol.toStringTag] = 'BigInt' + expect(isBigInt(notSoFakeBigInt)).toBe(false) + }, + ) + + it('object with toString returning 42n is not a BigInt', () => { + const fakeBigIntString = { + toString: function () { + return '42n' + }, + } + expect(isBigInt(fakeBigIntString)).toBe(false) + }) + }) + + describe('BigInt support', { skip: !hasBigInts }, () => { + it('BigInt primitives are BigInts', () => { + expect(isBigInt(BigInt(0))).toBe(true) + expect(isBigInt(BigInt(42))).toBe(true) + expect(isBigInt(BigInt(-1))).toBe(true) + }) + + it('wrapped BigInt is a BigInt', () => { + expect(isBigInt(Object(BigInt(42)))).toBe(true) + }) + }) +}) diff --git a/test/npm/is-core-module.test.mts b/test/npm/is-core-module.test.mts new file mode 100644 index 00000000..a5382e45 --- /dev/null +++ b/test/npm/is-core-module.test.mts @@ -0,0 +1,70 @@ +/** + * @fileoverview Tests for is-core-module NPM package override. + * Ported 1:1 from upstream v2.16.1 (9d91e714): + * https://github.com/inspect-js/is-core-module/blob/9d91e714e294b2ee41c1b6beae1ad06aacdaf5ca/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isCore, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('isCore()', () => { + it('fs is a core module', () => { + expect(isCore('fs')).toBe(true) + }) + + it('net is a core module', () => { + expect(isCore('net')).toBe(true) + }) + + it('http is a core module', () => { + expect(isCore('http')).toBe(true) + }) + + it('seq is not a core module', () => { + expect(isCore('seq')).toBe(false) + }) + + it('../ is not a core module', () => { + expect(isCore('../')).toBe(false) + }) + + it('toString is not a core module', () => { + expect(isCore('toString')).toBe(false) + }) + }) + + describe('core list', () => { + it('known core modules can be required', () => { + const knownCore = ['fs', 'path', 'http', 'https', 'url', 'os', 'util'] + for (const mod of knownCore) { + expect(isCore(mod)).toBe(true) + } + }) + + it('non-core modules are detected', () => { + const nonCore = ['express', 'lodash', 'react', 'not-a-module'] + for (const mod of nonCore) { + expect(isCore(mod)).toBe(false) + } + }) + }) + + describe('node: prefix', () => { + it('node:fs is a core module', () => { + expect(isCore('node:fs')).toBe(true) + }) + + it('node:path is a core module', () => { + expect(isCore('node:path')).toBe(true) + }) + }) +}) diff --git a/test/npm/is-generator-function.test.mts b/test/npm/is-generator-function.test.mts new file mode 100644 index 00000000..8e03c62a --- /dev/null +++ b/test/npm/is-generator-function.test.mts @@ -0,0 +1,101 @@ +/** + * @fileoverview Tests for is-generator-function NPM package override. + * Ported 1:1 from upstream v1.1.2 (625a966a): + * https://github.com/inspect-js/is-generator-function/blob/625a966a49815f5dd4aa484acd118b49c9ea8fc8/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isGeneratorFunction, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasToStringTag = + typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('returns false for non-functions', () => { + it.each([ + true, + false, + null, + undefined, + {}, + [], + /a/g, + 'string', + 42, + new Date(), + ])('returns false for %s', nonFunc => { + expect(isGeneratorFunction(nonFunc)).toBe(false) + }) + }) + + describe('returns false for non-generator functions', () => { + it('anonymous function is not a generator function', () => { + expect(isGeneratorFunction(function () {})).toBe(false) + }) + + it('named function is not a generator function', () => { + expect(isGeneratorFunction(function foo() {})).toBe(false) + }) + }) + + describe('returns false for non-generator function with faked toString', () => { + it('faked toString does not fool the check', () => { + const func = function () {} + func.toString = function () { + return 'function* () { return "TOTALLY REAL I SWEAR!"; }' + } + expect(isGeneratorFunction(func)).toBe(false) + }) + }) + + describe( + 'returns false for non-generator function with faked @@toStringTag', + { skip: !hasToStringTag }, + () => { + it('faked GeneratorFunction toStringTag does not fool the check', () => { + let genFunc: GeneratorFunction | undefined + try { + genFunc = new Function( + 'return function* () {}', + )() as GeneratorFunction + } catch (_e) { + // generator functions not supported + } + if (genFunc) { + const fakeGenFunction: Record = { + toString: function () { + return String(genFunc) + }, + valueOf: function () { + return genFunc + }, + } + fakeGenFunction[Symbol.toStringTag] = 'GeneratorFunction' + expect(isGeneratorFunction(fakeGenFunction)).toBe(false) + } + }) + }, + ) + + describe('returns true for generator functions', () => { + it('generator function is detected', () => { + let genFunc: GeneratorFunction | undefined + try { + genFunc = new Function('return function* () {}')() as GeneratorFunction + } catch (_e) { + // generator functions not supported + } + if (genFunc) { + expect(isGeneratorFunction(genFunc)).toBe(true) + } + }) + }) +}) diff --git a/test/npm/is-map.test.mts b/test/npm/is-map.test.mts new file mode 100644 index 00000000..597718a5 --- /dev/null +++ b/test/npm/is-map.test.mts @@ -0,0 +1,67 @@ +/** + * @fileoverview Tests for is-map NPM package override. + * Ported 1:1 from upstream v2.0.3 (8e53e508): + * https://github.com/inspect-js/is-map/blob/8e53e50836254a8589aae5620019f6eaccb8729b/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isMap, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-collections', () => { + it.each([ + null, + undefined, + true, + false, + 42, + 0, + -0, + NaN, + Infinity, + '', + 'foo', + /a/g, + [], + {}, + ])('returns false for %s', nonCollection => { + expect(isMap(nonCollection)).toBe(false) + }) + + it('returns false for a function', () => { + expect(isMap(function () {})).toBe(false) + }) + }) + + describe('Maps', { skip: typeof Map !== 'function' }, () => { + it('Map is a Map', () => { + expect(isMap(new Map())).toBe(true) + }) + }) + + describe('Sets', { skip: typeof Set !== 'function' }, () => { + it('Set is not a Map', () => { + expect(isMap(new Set())).toBe(false) + }) + }) + + describe('WeakMaps', { skip: typeof WeakMap !== 'function' }, () => { + it('WeakMap is not a Map', () => { + expect(isMap(new WeakMap())).toBe(false) + }) + }) + + describe('WeakSets', { skip: typeof WeakSet !== 'function' }, () => { + it('WeakSet is not a Map', () => { + expect(isMap(new WeakSet())).toBe(false) + }) + }) +}) diff --git a/test/npm/is-nan.test.mts b/test/npm/is-nan.test.mts new file mode 100644 index 00000000..56bb2b13 --- /dev/null +++ b/test/npm/is-nan.test.mts @@ -0,0 +1,96 @@ +/** + * @fileoverview Tests for is-nan NPM package override. + * Ported 1:1 from upstream v1.3.2 (96ed4e7b): + * https://github.com/es-shims/is-nan/blob/96ed4e7bf4482c535782ced50f2eca11fce55d42/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: numberIsNaN, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not NaN', () => { + describe('primitives', () => { + it('undefined is not NaN', () => { + expect(numberIsNaN(undefined)).toBe(false) + }) + + it('null is not NaN', () => { + expect(numberIsNaN(null)).toBe(false) + }) + + it('false is not NaN', () => { + expect(numberIsNaN(false)).toBe(false) + }) + + it('true is not NaN', () => { + expect(numberIsNaN(true)).toBe(false) + }) + + it('positive zero is not NaN', () => { + expect(numberIsNaN(0)).toBe(false) + }) + + it('Infinity is not NaN', () => { + expect(numberIsNaN(Infinity)).toBe(false) + }) + + it('-Infinity is not NaN', () => { + expect(numberIsNaN(-Infinity)).toBe(false) + }) + + it('string is not NaN', () => { + expect(numberIsNaN('foo')).toBe(false) + }) + + it('string NaN is not NaN', () => { + expect(numberIsNaN('NaN')).toBe(false) + }) + }) + + it('array is not NaN', () => { + expect(numberIsNaN([])).toBe(false) + }) + + it('object is not NaN', () => { + expect(numberIsNaN({})).toBe(false) + }) + + it('function is not NaN', () => { + expect(numberIsNaN(function () {})).toBe(false) + }) + + describe('valueOf', () => { + it('object with valueOf of NaN, converted to Number, is NaN', () => { + const obj = { + valueOf: function () { + return NaN + }, + } + expect(numberIsNaN(Number(obj))).toBe(true) + }) + + it('object with valueOf of NaN is not NaN', () => { + const obj = { + valueOf: function () { + return NaN + }, + } + expect(numberIsNaN(obj)).toBe(false) + }) + }) + }) + + describe('NaN literal', () => { + it('NaN is NaN', () => { + expect(numberIsNaN(NaN)).toBe(true) + }) + }) +}) diff --git a/test/npm/is-negative-zero.test.mts b/test/npm/is-negative-zero.test.mts new file mode 100644 index 00000000..7e3ad011 --- /dev/null +++ b/test/npm/is-negative-zero.test.mts @@ -0,0 +1,78 @@ +/** + * @fileoverview Tests for is-negative-zero NPM package override. + * Ported 1:1 from upstream v2.0.3 (8a42c03b): + * https://github.com/inspect-js/is-negative-zero/blob/8a42c03bc623e5ceaf3f6e9e001a36d99e019649/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isNegativeZero, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not negative zero', () => { + it('undefined is not negative zero', () => { + expect(isNegativeZero(undefined)).toBe(false) + }) + + it('null is not negative zero', () => { + expect(isNegativeZero(null)).toBe(false) + }) + + it('false is not negative zero', () => { + expect(isNegativeZero(false)).toBe(false) + }) + + it('true is not negative zero', () => { + expect(isNegativeZero(true)).toBe(false) + }) + + it('positive zero is not negative zero', () => { + expect(isNegativeZero(0)).toBe(false) + }) + + it('Infinity is not negative zero', () => { + expect(isNegativeZero(Infinity)).toBe(false) + }) + + it('-Infinity is not negative zero', () => { + expect(isNegativeZero(-Infinity)).toBe(false) + }) + + it('NaN is not negative zero', () => { + expect(isNegativeZero(NaN)).toBe(false) + }) + + it('string is not negative zero', () => { + expect(isNegativeZero('foo')).toBe(false) + }) + + it('array is not negative zero', () => { + expect(isNegativeZero([])).toBe(false) + }) + + it('object is not negative zero', () => { + expect(isNegativeZero({})).toBe(false) + }) + + it('function is not negative zero', () => { + expect(isNegativeZero(function () {})).toBe(false) + }) + + it('-1 is not negative zero', () => { + expect(isNegativeZero(-1)).toBe(false) + }) + }) + + describe('negative zero', () => { + it('negative zero is negative zero', () => { + expect(isNegativeZero(-0)).toBe(true) + }) + }) +}) diff --git a/test/npm/is-set.test.mts b/test/npm/is-set.test.mts new file mode 100644 index 00000000..28c75be0 --- /dev/null +++ b/test/npm/is-set.test.mts @@ -0,0 +1,67 @@ +/** + * @fileoverview Tests for is-set NPM package override. + * Ported 1:1 from upstream v2.0.3 (150c3cc4): + * https://github.com/inspect-js/is-set/blob/150c3cc40592eb269c51ee831a271ecc0d24d974/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isSet, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-collections', () => { + it.each([ + null, + undefined, + true, + false, + 42, + 0, + -0, + NaN, + Infinity, + '', + 'foo', + /a/g, + [], + {}, + ])('returns false for %s', nonCollection => { + expect(isSet(nonCollection)).toBe(false) + }) + + it('returns false for a function', () => { + expect(isSet(function () {})).toBe(false) + }) + }) + + describe('Maps', { skip: typeof Map !== 'function' }, () => { + it('Map is not a Set', () => { + expect(isSet(new Map())).toBe(false) + }) + }) + + describe('Sets', { skip: typeof Set !== 'function' }, () => { + it('Set is a Set', () => { + expect(isSet(new Set())).toBe(true) + }) + }) + + describe('WeakMaps', { skip: typeof WeakMap !== 'function' }, () => { + it('WeakMap is not a Set', () => { + expect(isSet(new WeakMap())).toBe(false) + }) + }) + + describe('WeakSets', { skip: typeof WeakSet !== 'function' }, () => { + it('WeakSet is not a Set', () => { + expect(isSet(new WeakSet())).toBe(false) + }) + }) +}) diff --git a/test/npm/is-shared-array-buffer.test.mts b/test/npm/is-shared-array-buffer.test.mts new file mode 100644 index 00000000..6b097e18 --- /dev/null +++ b/test/npm/is-shared-array-buffer.test.mts @@ -0,0 +1,86 @@ +/** + * @fileoverview Tests for is-shared-array-buffer NPM package override. + * Ported 1:1 from upstream v1.0.4 (9ccbf054): + * https://github.com/inspect-js/is-shared-array-buffer/blob/9ccbf054625ea8b05f753bec04a510f091147d2b/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isSharedArrayBuffer, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof isSharedArrayBuffer).toBe('function') + }) + + it('returns false for non-SharedArrayBuffer values', () => { + const nonSABs: unknown[] = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + {}, + [], + function () {}, + /a/g, + ] + for (const nonSAB of nonSABs) { + expect(isSharedArrayBuffer(nonSAB)).toBe(false) + } + }) + + describe( + 'actual SharedArrayBuffer instances', + { skip: typeof SharedArrayBuffer === 'undefined' }, + () => { + it('SharedArrayBuffer is a SharedArrayBuffer', () => { + const sab = new SharedArrayBuffer(0) + expect(isSharedArrayBuffer(sab)).toBe(true) + }) + }, + ) + + describe('Typed Arrays', () => { + const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', + ] + + for (const name of typedArrayNames) { + const TypedArray = (globalThis as any)[name] + if (typeof TypedArray === 'function') { + it(`${name} buffer is not a SharedArrayBuffer`, () => { + const ta = new TypedArray(0) + expect(isSharedArrayBuffer(ta.buffer)).toBe(false) + }) + + it(`${name} instance is not a SharedArrayBuffer`, () => { + const ta = new TypedArray(0) + expect(isSharedArrayBuffer(ta)).toBe(false) + }) + } + } + }) +}) diff --git a/test/npm/is-symbol.test.mts b/test/npm/is-symbol.test.mts new file mode 100644 index 00000000..6399ae87 --- /dev/null +++ b/test/npm/is-symbol.test.mts @@ -0,0 +1,118 @@ +/** + * @fileoverview Tests for is-symbol NPM package override. + * Ported 1:1 from upstream v1.1.1 (b1561d99): + * https://github.com/inspect-js/is-symbol/blob/b1561d99da494df9bd1a768b1444ce9d1bc8ac1a/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isSymbol, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasSymbols = typeof Symbol === 'function' +const hasToStringTag = hasSymbols && typeof Symbol.toStringTag === 'symbol' + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-symbol values', () => { + it.each([ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + {}, + [], + /a/g, + new Date(), + ])('returns false for %s', nonSymbol => { + expect(isSymbol(nonSymbol)).toBe(false) + }) + + it('returns false for wrapped booleans', () => { + expect(isSymbol(Object(true))).toBe(false) + expect(isSymbol(Object(false))).toBe(false) + }) + + it('returns false for a function', () => { + expect(isSymbol(function () {})).toBe(false) + }) + }) + + describe('faked symbol values', () => { + it( + 'object with valueOf returning a symbol is not a symbol', + { skip: !hasSymbols }, + () => { + const fakeSymbol = { + valueOf: function () { + return Symbol('foo') + }, + } + expect(isSymbol(fakeSymbol)).toBe(false) + }, + ) + + it( + 'faked @@toStringTag does not fool the check', + { skip: !hasToStringTag }, + () => { + const fakeSymbol: Record = { + valueOf: function () { + return Symbol('foo') + }, + } + fakeSymbol[Symbol.toStringTag] = 'Symbol' + expect(isSymbol(fakeSymbol)).toBe(false) + + const notSoFakeSymbol: Record = { + valueOf: function () { + return 42 + }, + } + notSoFakeSymbol[Symbol.toStringTag] = 'Symbol' + expect(isSymbol(notSoFakeSymbol)).toBe(false) + }, + ) + + it('object with toString returning Symbol(foo) is not a symbol', () => { + const fakeSymbolString = { + toString: function () { + return 'Symbol(foo)' + }, + } + expect(isSymbol(fakeSymbolString)).toBe(false) + }) + }) + + describe('Symbol support', { skip: !hasSymbols }, () => { + it('well-known Symbols are symbols', () => { + const isWellKnown = (name: string) => name !== 'for' && name !== 'keyFor' + const wellKnownSymbols = + Object.getOwnPropertyNames(Symbol).filter(isWellKnown) + for (const name of wellKnownSymbols) { + const sym = (Symbol as any)[name] + if (typeof sym === 'symbol') { + expect(isSymbol(sym)).toBe(true) + } + } + }) + + it('user-created symbols are symbols', () => { + expect(isSymbol(Symbol())).toBe(true) + expect(isSymbol(Symbol('foo'))).toBe(true) + expect(isSymbol(Symbol.for('foo'))).toBe(true) + expect(isSymbol(Object(Symbol('object')))).toBe(true) + }) + }) +}) diff --git a/test/npm/is-typed-array.test.mts b/test/npm/is-typed-array.test.mts new file mode 100644 index 00000000..de820db9 --- /dev/null +++ b/test/npm/is-typed-array.test.mts @@ -0,0 +1,108 @@ +/** + * @fileoverview Tests for is-typed-array NPM package override. + * Ported 1:1 from upstream v1.1.15 (4f2611d5): + * https://github.com/inspect-js/is-typed-array/blob/4f2611d57b40760c01748181c4685c37145c6521/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isTypedArray, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasToStringTag = + typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol' + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not arrays', () => { + it('non-number/string primitives', () => { + expect(isTypedArray(undefined)).toBe(false) + expect(isTypedArray(null)).toBe(false) + expect(isTypedArray(false)).toBe(false) + expect(isTypedArray(true)).toBe(false) + }) + + it('object is not typed array', () => { + expect(isTypedArray({})).toBe(false) + }) + + it('regex literal is not typed array', () => { + expect(isTypedArray(/a/g)).toBe(false) + }) + + it('regex object is not typed array', () => { + expect(isTypedArray(new RegExp('a', 'g'))).toBe(false) + }) + + it('new Date() is not typed array', () => { + expect(isTypedArray(new Date())).toBe(false) + }) + + it('numbers are not typed arrays', () => { + expect(isTypedArray(42)).toBe(false) + expect(isTypedArray(Object(42))).toBe(false) + expect(isTypedArray(NaN)).toBe(false) + expect(isTypedArray(Infinity)).toBe(false) + }) + + it('strings are not typed arrays', () => { + expect(isTypedArray('foo')).toBe(false) + expect(isTypedArray(Object('foo'))).toBe(false) + }) + }) + + describe('Functions', () => { + it('function is not typed array', () => { + expect(isTypedArray(function () {})).toBe(false) + }) + }) + + describe('@@toStringTag', { skip: !hasToStringTag }, () => { + it('faked typed arrays are not typed arrays', () => { + for (const name of typedArrayNames) { + if (typeof (globalThis as any)[name] === 'function') { + const fakeTypedArray: any = [] + fakeTypedArray[Symbol.toStringTag] = name + expect(isTypedArray(fakeTypedArray)).toBe(false) + } + } + }) + }) + + describe('non-Typed Arrays', () => { + it('[] is not typed array', () => { + expect(isTypedArray([])).toBe(false) + }) + }) + + describe('Typed Arrays', () => { + for (const name of typedArrayNames) { + const TypedArray = (globalThis as any)[name] + if (typeof TypedArray === 'function') { + it(`new ${name}(10) is typed array`, () => { + const arr = new TypedArray(10) + expect(isTypedArray(arr)).toBe(true) + }) + } + } + }) +}) diff --git a/test/npm/is-weakmap.test.mts b/test/npm/is-weakmap.test.mts new file mode 100644 index 00000000..697830c5 --- /dev/null +++ b/test/npm/is-weakmap.test.mts @@ -0,0 +1,67 @@ +/** + * @fileoverview Tests for is-weakmap NPM package override. + * Ported 1:1 from upstream v2.0.2 (a747afa3): + * https://github.com/inspect-js/is-weakmap/blob/a747afa315110eb6849e5be97686a577d2f16953/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isWeakMap, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-collections', () => { + it.each([ + null, + undefined, + true, + false, + 42, + 0, + -0, + NaN, + Infinity, + '', + 'foo', + /a/g, + [], + {}, + ])('returns false for %s', nonCollection => { + expect(isWeakMap(nonCollection)).toBe(false) + }) + + it('returns false for a function', () => { + expect(isWeakMap(function () {})).toBe(false) + }) + }) + + describe('Maps', { skip: typeof Map !== 'function' }, () => { + it('Map is not a WeakMap', () => { + expect(isWeakMap(new Map())).toBe(false) + }) + }) + + describe('Sets', { skip: typeof Set !== 'function' }, () => { + it('Set is not a WeakMap', () => { + expect(isWeakMap(new Set())).toBe(false) + }) + }) + + describe('WeakMaps', { skip: typeof WeakMap !== 'function' }, () => { + it('WeakMap is a WeakMap', () => { + expect(isWeakMap(new WeakMap())).toBe(true) + }) + }) + + describe('WeakSets', { skip: typeof WeakSet !== 'function' }, () => { + it('WeakSet is not a WeakMap', () => { + expect(isWeakMap(new WeakSet())).toBe(false) + }) + }) +}) diff --git a/test/npm/is-weakref.test.mts b/test/npm/is-weakref.test.mts new file mode 100644 index 00000000..2d41ede1 --- /dev/null +++ b/test/npm/is-weakref.test.mts @@ -0,0 +1,49 @@ +/** + * @fileoverview Tests for is-weakref NPM package override. + * Ported 1:1 from upstream v1.1.1 (d20d4798): + * https://github.com/inspect-js/is-weakref/blob/d20d47987837a1178a83893b8766e8ec0cc574e4/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isWeakRef, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof isWeakRef).toBe('function') + }) + + it('returns false for non-WeakRef values', () => { + const nonWeakRefs = [ + undefined, + null, + true, + false, + 42, + 0, + Infinity, + NaN, + /a/g, + function () {}, + {}, + [], + ] + for (const nonWeakRef of nonWeakRefs) { + expect(isWeakRef(nonWeakRef)).toBe(false) + } + }) + + describe('actual WeakRefs', { skip: typeof WeakRef === 'undefined' }, () => { + it('WeakRef is a WeakRef', () => { + const ref = new WeakRef({}) + expect(isWeakRef(ref)).toBe(true) + }) + }) +}) diff --git a/test/npm/is-weakset.test.mts b/test/npm/is-weakset.test.mts new file mode 100644 index 00000000..66133a19 --- /dev/null +++ b/test/npm/is-weakset.test.mts @@ -0,0 +1,67 @@ +/** + * @fileoverview Tests for is-weakset NPM package override. + * Ported 1:1 from upstream v2.0.4 (a63f2927): + * https://github.com/inspect-js/is-weakset/blob/a63f292737acdc9e0df55284317a98c1bf0be90d/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isWeakSet, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('non-collections', () => { + it.each([ + null, + undefined, + true, + false, + 42, + 0, + -0, + NaN, + Infinity, + '', + 'foo', + /a/g, + [], + {}, + ])('returns false for %s', nonCollection => { + expect(isWeakSet(nonCollection)).toBe(false) + }) + + it('returns false for a function', () => { + expect(isWeakSet(function () {})).toBe(false) + }) + }) + + describe('Maps', { skip: typeof Map !== 'function' }, () => { + it('Map is not a WeakSet', () => { + expect(isWeakSet(new Map())).toBe(false) + }) + }) + + describe('Sets', { skip: typeof Set !== 'function' }, () => { + it('Set is not a WeakSet', () => { + expect(isWeakSet(new Set())).toBe(false) + }) + }) + + describe('WeakMaps', { skip: typeof WeakMap !== 'function' }, () => { + it('WeakMap is not a WeakSet', () => { + expect(isWeakSet(new WeakMap())).toBe(false) + }) + }) + + describe('WeakSets', { skip: typeof WeakSet !== 'function' }, () => { + it('WeakSet is a WeakSet', () => { + expect(isWeakSet(new WeakSet())).toBe(true) + }) + }) +}) diff --git a/test/npm/isarray.test.mts b/test/npm/isarray.test.mts new file mode 100644 index 00000000..264eb7c2 --- /dev/null +++ b/test/npm/isarray.test.mts @@ -0,0 +1,78 @@ +/** + * @fileoverview Tests for isarray NPM package override. + * Ported 1:1 from upstream v2.0.5 (juliangruber/isarray): + * https://github.com/juliangruber/isarray/blob/v2.0.5/test.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: isArray, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('[] is an array', () => { + expect(isArray([])).toBe(true) + }) + + it('{} is not an array', () => { + expect(isArray({})).toBe(false) + }) + + it('null is not an array', () => { + expect(isArray(null)).toBe(false) + }) + + it('false is not an array', () => { + expect(isArray(false)).toBe(false) + }) + + it('empty string is not an array', () => { + expect(isArray('')).toBe(false) + }) + + it('"42" is not an array', () => { + expect(isArray('42')).toBe(false) + }) + + it('42 is not an array', () => { + expect(isArray(42)).toBe(false) + }) + + it('34.00 is not an array', () => { + expect(isArray(34.0)).toBe(false) + }) + + it('123e-5 is not an array', () => { + expect(isArray(123e-5)).toBe(false) + }) + + it('"[]" is not an array', () => { + expect(isArray('[]')).toBe(false) + }) + + it('undefined is not an array', () => { + expect(isArray(undefined)).toBe(false) + }) + + it('function is not an array', () => { + expect(isArray(function () {})).toBe(false) + }) + + it('object with numeric key is not an array', () => { + const obj: Record = {} + obj[0] = true + expect(isArray(obj)).toBe(false) + }) + + it('array with extra property is still an array', () => { + const arr: any = [] + arr.foo = 'bar' + expect(isArray(arr)).toBe(true) + }) +}) diff --git a/test/npm/iterator.prototype.test.mts b/test/npm/iterator.prototype.test.mts new file mode 100644 index 00000000..78dd038c --- /dev/null +++ b/test/npm/iterator.prototype.test.mts @@ -0,0 +1,56 @@ +/** + * @fileoverview Tests for iterator.prototype NPM package override. + * Ported 1:1 from upstream v1.1.5 (9cd8a50f): + * https://github.com/ljharb/Iterator.prototype/blob/9cd8a50f98006f3d13da871a971c174ea29edb0c/test/index.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: iterProto, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is truthy', () => { + expect(iterProto).toBeTruthy() + }) + + it('is an object', () => { + expect(typeof iterProto).toBe('object') + }) + + it('is not Object.prototype', () => { + expect(iterProto).not.toBe(Object.prototype) + }) + + it('Symbol.iterator returns receiver', () => { + const fn = iterProto[Symbol.iterator] + expect(typeof fn).toBe('function') + + const sentinel = {} + expect(fn.call(sentinel)).toBe(sentinel) + }) + + it('ArrayIterator [[Prototype]] is Iterator.prototype', () => { + const grandProto = Object.getPrototypeOf(Object.getPrototypeOf([].keys())) + if (grandProto === Object.prototype) { + return + } + expect(grandProto).toBe(iterProto) + }) + + it('SetIterator [[Prototype]] is Iterator.prototype', () => { + const grandProto = Object.getPrototypeOf( + Object.getPrototypeOf(new Set().keys()), + ) + if (grandProto === Object.prototype) { + return + } + expect(grandProto).toBe(iterProto) + }) +}) diff --git a/test/npm/jsonify.test.mts b/test/npm/jsonify.test.mts new file mode 100644 index 00000000..3b2592b9 --- /dev/null +++ b/test/npm/jsonify.test.mts @@ -0,0 +1,57 @@ +/** + * @fileoverview Tests for jsonify NPM package override. + * Ported 1:1 from upstream v0.0.1 (7629309d): + * https://github.com/ljharb/jsonify/blob/7629309dc2f145cdd7680ea174979330f1e39855/test/stringify.js + * https://github.com/ljharb/jsonify/blob/7629309dc2f145cdd7680ea174979330f1e39855/test/parse.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: json, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('parse', () => { + it('parses values the same as JSON.parse', () => { + const testValues = [ + '42', + '"hello"', + 'true', + 'false', + 'null', + '[1,2,3]', + '{"a":1,"b":2}', + '{"nested":{"key":"value"}}', + '[1,"two",true,null]', + ] + for (const s of testValues) { + expect(json.parse(s)).toEqual(JSON.parse(s)) + } + }) + }) + + describe('stringify', () => { + it('stringifies values the same as JSON.stringify', () => { + const testValues = [ + 42, + 'hello', + true, + false, + null, + [1, 2, 3], + { a: 1, b: 2 }, + { nested: { key: 'value' } }, + [1, 'two', true, null], + ] + for (const obj of testValues) { + expect(json.stringify(obj)).toBe(JSON.stringify(obj)) + } + }) + }) +}) diff --git a/test/npm/number-is-nan.test.mts b/test/npm/number-is-nan.test.mts new file mode 100644 index 00000000..bded905d --- /dev/null +++ b/test/npm/number-is-nan.test.mts @@ -0,0 +1,36 @@ +/** + * @fileoverview Tests for number-is-nan NPM package override. + * Ported 1:1 from upstream v1.0.1 (8982e8c4): + * https://github.com/sindresorhus/number-is-nan/blob/8982e8c42c724434bfa107f8bb65c19d4b16930d/test.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: numberIsNan, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('returns true for NaN', () => { + expect(numberIsNan(NaN)).toBe(true) + }) + + it('returns false for non-NaN values', () => { + expect(numberIsNan()).toBe(false) + expect(numberIsNan(true)).toBe(false) + expect(numberIsNan(false)).toBe(false) + expect(numberIsNan(null)).toBe(false) + expect(numberIsNan(0)).toBe(false) + expect(numberIsNan(Infinity)).toBe(false) + expect(numberIsNan(-Infinity)).toBe(false) + expect(numberIsNan('')).toBe(false) + expect(numberIsNan('unicorn')).toBe(false) + expect(numberIsNan({})).toBe(false) + expect(numberIsNan([])).toBe(false) + }) +}) diff --git a/test/npm/object-assign.test.mts b/test/npm/object-assign.test.mts new file mode 100644 index 00000000..c0f7461f --- /dev/null +++ b/test/npm/object-assign.test.mts @@ -0,0 +1,114 @@ +/** + * @fileoverview Tests for object-assign NPM package override. + * Ported 1:1 from upstream v4.1.1 (a29ce505): + * https://github.com/sindresorhus/object-assign/blob/a29ce5053398661d882e368e60da6553d3d82434/test.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: objectAssign, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('has the correct length', () => { + expect(objectAssign.length).toBe(2) + }) + + it('throws when target is not an object', () => { + expect(() => objectAssign(null)).toThrow(TypeError) + expect(() => objectAssign(undefined)).toThrow(TypeError) + }) + + it('assigns own enumerable properties from source to target object', () => { + expect(objectAssign({ foo: 0 }, { bar: 1 })).toEqual({ foo: 0, bar: 1 }) + expect(objectAssign({ foo: 0 }, null, undefined)).toEqual({ foo: 0 }) + expect(objectAssign({ foo: 0 }, null, undefined, { bar: 1 }, null)).toEqual( + { foo: 0, bar: 1 }, + ) + }) + + it('throws on null/undefined target', () => { + expect(() => objectAssign(null, {})).toThrow() + expect(() => objectAssign(undefined, {})).toThrow() + expect(() => objectAssign(undefined, undefined)).toThrow() + }) + + it('does not throw on null/undefined sources', () => { + expect(() => objectAssign({}, null)).not.toThrow() + expect(() => objectAssign({}, undefined)).not.toThrow() + expect(() => objectAssign({}, undefined, null)).not.toThrow() + }) + + it('supports multiple sources', () => { + expect(objectAssign({ foo: 0 }, { bar: 1 }, { bar: 2 })).toEqual({ + foo: 0, + bar: 2, + }) + expect(objectAssign({}, {}, { foo: 1 })).toEqual({ foo: 1 }) + }) + + it('only iterates own keys', () => { + const Unicorn = function (this: any) {} as any + Unicorn.prototype.rainbows = 'many' + const unicorn = new Unicorn() + unicorn.bar = 1 + + expect(objectAssign({ foo: 1 }, unicorn)).toEqual({ foo: 1, bar: 1 }) + }) + + it('returns the modified target object', () => { + const target = {} + const returned = objectAssign(target, { a: 1 }) + expect(returned).toBe(target) + }) + + it('supports Object.create(null) objects', () => { + const obj = Object.create(null) + obj.foo = true + expect(objectAssign({}, obj)).toEqual({ foo: true }) + }) + + it('preserves property order', () => { + const letters = 'abcdefghijklmnopqrst' + const source: Record = {} + letters.split('').forEach(letter => { + source[letter] = letter + }) + const target = objectAssign({}, source) + expect(Object.keys(target).join('')).toBe(letters) + }) + + it('accepts primitives as target', () => { + const target = objectAssign('abcdefg', { foo: 'bar' }) + const strObj = Object('abcdefg') + strObj.foo = 'bar' + expect(target).toEqual(strObj) + }) + + it('supports symbol properties', () => { + const target = {} + const source: Record = {} + const sym = Symbol('foo') + source[sym] = 'bar' + objectAssign(target, source) + expect((target as any)[sym]).toBe('bar') + }) + + it('only copies enumerable symbols', () => { + const target = {} + const source = {} + const sym = Symbol('foo') + Object.defineProperty(source, sym, { + enumerable: false, + value: 'bar', + }) + objectAssign(target, source) + expect((target as any)[sym]).toBe(undefined) + }) +}) diff --git a/test/npm/object-is.test.mts b/test/npm/object-is.test.mts new file mode 100644 index 00000000..f1d47c29 --- /dev/null +++ b/test/npm/object-is.test.mts @@ -0,0 +1,125 @@ +/** + * @fileoverview Tests for object-is NPM package override. + * Ported 1:1 from upstream v1.1.6 (de5db308): + * https://github.com/es-shims/object-is/blob/de5db308d01cf04294a71022c26e0b6d1b0d7ad6/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: is, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('works with primitives', () => { + it('two absent args are the same', () => { + expect(is()).toBe(true) + }) + + it('undefined & one absent arg are the same', () => { + expect(is(undefined)).toBe(true) + }) + + it('undefined is undefined', () => { + expect(is(undefined, undefined)).toBe(true) + }) + + it('null is null', () => { + expect(is(null, null)).toBe(true) + }) + + it('true is true', () => { + expect(is(true, true)).toBe(true) + }) + + it('false is false', () => { + expect(is(false, false)).toBe(true) + }) + + it('true is not false', () => { + expect(is(true, false)).toBe(false) + }) + }) + + describe('works with NaN', () => { + it('NaN is NaN', () => { + expect(is(NaN, NaN)).toBe(true) + }) + }) + + describe('differentiates zeroes', () => { + it('+0 is +0', () => { + expect(is(0, 0)).toBe(true) + }) + + it('-0 is -0', () => { + expect(is(-0, -0)).toBe(true) + }) + + it('+0 is not -0', () => { + expect(is(0, -0)).toBe(false) + }) + }) + + describe('nonzero numbers', () => { + it('infinity is infinity', () => { + expect(is(Infinity, Infinity)).toBe(true) + }) + + it('-infinity is -infinity', () => { + expect(is(-Infinity, -Infinity)).toBe(true) + }) + + it('42 is 42', () => { + expect(is(42, 42)).toBe(true) + }) + + it('42 is not -42', () => { + expect(is(42, -42)).toBe(false) + }) + }) + + describe('strings', () => { + it('empty string is empty string', () => { + expect(is('', '')).toBe(true) + }) + + it('string is string', () => { + expect(is('foo', 'foo')).toBe(true) + }) + + it('string is not different string', () => { + expect(is('foo', 'bar')).toBe(false) + }) + }) + + describe('objects', () => { + it('object is same object', () => { + const obj = {} + expect(is(obj, obj)).toBe(true) + }) + + it('object is not different object', () => { + expect(is({}, {})).toBe(false) + }) + }) + + describe('Symbols', () => { + it('Symbol.iterator is itself', () => { + expect(is(Symbol.iterator, Symbol.iterator)).toBe(true) + }) + + it('different Symbols are not equal', () => { + expect(is(Symbol(), Symbol())).toBe(false) + }) + + it('Symbol.iterator is not boxed form of itself', () => { + expect(is(Symbol.iterator, Object(Symbol.iterator))).toBe(false) + }) + }) +}) diff --git a/test/npm/object-keys.test.mts b/test/npm/object-keys.test.mts index d8c6f476..94a2765b 100644 --- a/test/npm/object-keys.test.mts +++ b/test/npm/object-keys.test.mts @@ -158,7 +158,7 @@ describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { it('should work in environments with the dontEnum bug', () => { // eslint-disable-next-line unicorn/consistent-function-scoping - const Foo = () => {} + function Foo() {} Foo.prototype.a = () => {} expect(objectKeys(Foo.prototype)).toContain('a') }) diff --git a/test/npm/object.entries.test.mts b/test/npm/object.entries.test.mts new file mode 100644 index 00000000..b677bd49 --- /dev/null +++ b/test/npm/object.entries.test.mts @@ -0,0 +1,110 @@ +/** + * @fileoverview Tests for object.entries NPM package override. + * Ported 1:1 from upstream v1.1.8 (cff1adb0): + * https://github.com/es-shims/Object.entries/blob/cff1adb0bb7340cad95fe34bb5ea9989cd1f7c1e/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: entries, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + const a = {} + const b = {} + const c = {} + const obj = { a, b, c } + + it('basic support', () => { + expect(entries(obj)).toEqual([ + ['a', a], + ['b', b], + ['c', c], + ]) + }) + + it('duplicate entries are included', () => { + expect(entries({ a, b: a, c })).toEqual([ + ['a', a], + ['b', a], + ['c', c], + ]) + }) + + it('entries are in the same order as keys', () => { + const object: Record = { a, b } + object[0] = 3 + object['c'] = c + object[1] = 4 + delete object[0] + const objKeys = Object.keys(object) + const objEntries = objKeys.map(key => [key, object[key]] as [string, any]) + expect(entries(object)).toEqual(objEntries) + }) + + it('non-enumerable properties are omitted', () => { + const object = { a, b } + Object.defineProperty(object, 'c', { enumerable: false, value: c }) + expect(entries(object)).toEqual([ + ['a', a], + ['b', b], + ]) + }) + + it('inherited properties are omitted', () => { + const F = function (this: any) {} as any + F.prototype.a = a + const f = new F() + f.b = b + expect(entries(f)).toEqual([['b', b]]) + }) + + it('Symbol properties are omitted', () => { + const object: Record = { a, b, c } + const enumSym = Symbol('enum') + const nonEnumSym = Symbol('non enum') + object[enumSym] = enumSym + object['d'] = enumSym + Object.defineProperty(object, nonEnumSym, { + enumerable: false, + value: nonEnumSym, + }) + expect(entries(object)).toEqual([ + ['a', a], + ['b', b], + ['c', c], + ['d', enumSym], + ]) + }) + + it('not-yet-visited keys deleted on [[Get]] must not show up in output', () => { + const o: Record = { a: 1, b: 2, c: 3 } + Object.defineProperty(o, 'a', { + get() { + delete this.b + return 1 + }, + }) + expect(entries(o)).toEqual([ + ['a', 1], + ['c', 3], + ]) + }) + + it('not-yet-visited keys made non-enumerable on [[Get]] must not show up in output', () => { + const o: Record = { a: 'A', b: 'B' } + Object.defineProperty(o, 'a', { + get() { + Object.defineProperty(o, 'b', { enumerable: false }) + return 'A' + }, + }) + expect(entries(o)).toEqual([['a', 'A']]) + }) +}) diff --git a/test/npm/object.fromentries.test.mts b/test/npm/object.fromentries.test.mts new file mode 100644 index 00000000..9f381f05 --- /dev/null +++ b/test/npm/object.fromentries.test.mts @@ -0,0 +1,52 @@ +/** + * @fileoverview Tests for object.fromentries NPM package override. + * Ported 1:1 from upstream v2.0.8 (4889d056): + * https://github.com/es-shims/Object.fromEntries/blob/4889d0564c7bf79c53748e0021f3925d393c083b/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: fromEntries, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('converts entries to object', () => { + const a = {} + const b = {} + const c = {} + const entries: Array<[string, object]> = [ + ['a', a], + ['b', b], + ['c', c], + ] + const obj = { a, b, c } + expect(fromEntries(entries)).toEqual(obj) + }) + + it('throws on absent iterable', () => { + expect(() => fromEntries()).toThrow() + }) + + it('throws on undefined', () => { + expect(() => fromEntries(undefined)).toThrow() + }) + + it('throws on null', () => { + expect(() => fromEntries(null)).toThrow() + }) + + it('works with a duplicate key', () => { + expect( + fromEntries([ + ['foo', 1], + ['foo', 2], + ]), + ).toEqual({ foo: 2 }) + }) +}) diff --git a/test/npm/object.getownpropertydescriptors.test.mts b/test/npm/object.getownpropertydescriptors.test.mts new file mode 100644 index 00000000..df62f148 --- /dev/null +++ b/test/npm/object.getownpropertydescriptors.test.mts @@ -0,0 +1,93 @@ +/** + * @fileoverview Tests for object.getownpropertydescriptors NPM package override. + * Ported 1:1 from upstream v2.1.9 (d4bfaba1): + * https://github.com/es-shims/object.getownpropertydescriptors/blob/d4bfaba101663919e4066f0502f3083138ad1cc6/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: getDescriptors, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + const enumDescriptor = { + configurable: true, + enumerable: false, + value: true, + writable: false, + } + const writableDescriptor = { + configurable: true, + enumerable: true, + value: 42, + writable: true, + } + + it('gets all expected non-Symbol descriptors', () => { + const obj: Record = { normal: Infinity } + Object.defineProperty(obj, 'enumerable', enumDescriptor) + Object.defineProperty(obj, 'writable', writableDescriptor) + + const descriptors = getDescriptors(obj) + + expect(descriptors).toEqual({ + enumerable: enumDescriptor, + normal: { + configurable: true, + enumerable: true, + value: Infinity, + writable: true, + }, + writable: writableDescriptor, + }) + }) + + it('gets Symbol descriptors too', () => { + const symbol = Symbol('sym') + const symDescriptor = { + configurable: false, + enumerable: true, + value: [symbol], + writable: true, + } + const obj: Record = { normal: Infinity } + Object.defineProperty(obj, 'enumerable', enumDescriptor) + Object.defineProperty(obj, 'writable', writableDescriptor) + Object.defineProperty(obj, 'symbol', symDescriptor) + + const descriptors = getDescriptors(obj) + + expect(descriptors).toEqual({ + enumerable: enumDescriptor, + normal: { + configurable: true, + enumerable: true, + value: Infinity, + writable: true, + }, + symbol: symDescriptor, + writable: writableDescriptor, + }) + }) + + it('Proxies that return an undefined descriptor', () => { + const obj: Record = { foo: true } + const fooDescriptor = Object.getOwnPropertyDescriptor(obj, 'foo') + + const proxy = new Proxy(obj, { + getOwnPropertyDescriptor(target, key) { + return Object.getOwnPropertyDescriptor(target, key) + }, + ownKeys() { + return ['foo', 'bar'] + }, + }) + expect(getDescriptors(proxy)).toEqual({ foo: fooDescriptor }) + }) +}) diff --git a/test/npm/object.getprototypeof.test.mts b/test/npm/object.getprototypeof.test.mts new file mode 100644 index 00000000..7f5ef0d1 --- /dev/null +++ b/test/npm/object.getprototypeof.test.mts @@ -0,0 +1,53 @@ +/** + * @fileoverview Tests for object.getprototypeof NPM package override. + * Ported 1:1 from upstream v1.0.7 (9efa3c7f): + * https://github.com/es-shims/Object.getPrototypeOf/blob/9efa3c7f024b20f065ac420c4fa168ea7c3ce6c6/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: getPrototypeOf, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws for undefined', () => { + expect(() => getPrototypeOf(undefined)).toThrow(TypeError) + }) + + it('throws for null', () => { + expect(() => getPrototypeOf(null)).toThrow(TypeError) + }) + + it('returns Boolean.prototype for booleans', () => { + expect(getPrototypeOf(true)).toBe(Boolean.prototype) + expect(getPrototypeOf(false)).toBe(Boolean.prototype) + }) + + it('returns Number.prototype for numbers', () => { + expect(getPrototypeOf(42)).toBe(Number.prototype) + expect(getPrototypeOf(NaN)).toBe(Number.prototype) + expect(getPrototypeOf(0)).toBe(Number.prototype) + expect(getPrototypeOf(-0)).toBe(Number.prototype) + expect(getPrototypeOf(Infinity)).toBe(Number.prototype) + expect(getPrototypeOf(-Infinity)).toBe(Number.prototype) + }) + + it('returns String.prototype for strings', () => { + expect(getPrototypeOf('')).toBe(String.prototype) + expect(getPrototypeOf('foo')).toBe(String.prototype) + }) + + it('returns correct prototypes for objects', () => { + expect(getPrototypeOf(/a/g)).toBe(RegExp.prototype) + expect(getPrototypeOf(new Date())).toBe(Date.prototype) + expect(getPrototypeOf(function () {})).toBe(Function.prototype) + expect(getPrototypeOf([])).toBe(Array.prototype) + expect(getPrototypeOf({})).toBe(Object.prototype) + }) +}) diff --git a/test/npm/object.groupby.test.mts b/test/npm/object.groupby.test.mts new file mode 100644 index 00000000..c4e16978 --- /dev/null +++ b/test/npm/object.groupby.test.mts @@ -0,0 +1,79 @@ +/** + * @fileoverview Tests for object.groupby NPM package override. + * Ported 1:1 from upstream v1.0.3 (fa1c331c): + * https://github.com/es-shims/Object.groupBy/blob/fa1c331c346bc6e852a06d0f8fd7093be25846ab/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: groupBy, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('callback function', () => { + it('throws for non-function callbacks', () => { + const nonFunctions = [ + undefined, + null, + true, + false, + 0, + 42, + Infinity, + NaN, + '', + 'foo', + /a/g, + [], + {}, + ] + for (const nonFunction of nonFunctions) { + expect(() => groupBy([], nonFunction)).toThrow(TypeError) + } + }) + }) + + describe('grouping', () => { + it('an empty array produces an empty object', () => { + const result = groupBy([], () => 'a') + expect(result).toEqual({ __proto__: null }) + }) + + it('groups by parity', () => { + const arr = [0, -0, 1, 2, 3, 4, 5, NaN, Infinity, -Infinity] + const parity = (x: number) => { + if (x !== x) { + return undefined + } + if (!isFinite(x)) { + return '\u221E' + } + return x % 2 === 0 ? 'even' : 'odd' + } + const grouped = { + __proto__: null, + even: [0, -0, 2, 4], + odd: [1, 3, 5], + undefined: [NaN], + '\u221E': [Infinity, -Infinity], + } + expect(groupBy(arr, parity)).toEqual(grouped) + }) + + it('thisArg and callback arguments are as expected', () => { + const arr = [0, -0, 1, 2, 3, 4, 5, NaN, Infinity, -Infinity] + const result = groupBy(arr, function (this: any, x: any, i: number) { + expect(this).toBe(undefined) + expect(x).toBe(arr[i]) + return 42 + }) + expect(result).toEqual({ __proto__: null, 42: arr }) + }) + }) +}) diff --git a/test/npm/object.hasown.test.mts b/test/npm/object.hasown.test.mts new file mode 100644 index 00000000..14c9e08f --- /dev/null +++ b/test/npm/object.hasown.test.mts @@ -0,0 +1,62 @@ +/** + * @fileoverview Tests for object.hasown NPM package override. + * Ported 1:1 from upstream v1.1.4 (4e44c2dd): + * https://github.com/es-shims/Object.hasOwn/blob/4e44c2ddaedaa40c4054383b80209797555b54a6/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: hasOwn, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('checks ToObject first', () => { + const badPropertyKey = { + toString() { + throw new SyntaxError('nope') + }, + } + expect(() => hasOwn(null, badPropertyKey)).toThrow(TypeError) + }) + + it('checks ToPropertyKey next', () => { + const badPropertyKey = { + toString() { + throw new SyntaxError('nope') + }, + } + expect(() => hasOwn({}, badPropertyKey)).toThrow(SyntaxError) + }) + + it('toString is not an own property', () => { + const obj = { a: 1 } + expect('toString' in obj).toBe(true) + expect(hasOwn(obj, 'toString')).toBe(false) + }) + + it('own property is recognized', () => { + expect(hasOwn({ a: 1 }, 'a')).toBe(true) + }) + + it('non-enumerable own property is recognized', () => { + expect(hasOwn([], 'length')).toBe(true) + }) + + describe('Symbols', () => { + it('own symbol is recognized', () => { + const o: Record = {} + o[Symbol.iterator] = true + expect(hasOwn(o, Symbol.iterator)).toBe(true) + }) + + it('built-in own symbol is recognized', () => { + expect(hasOwn(Array.prototype, Symbol.iterator)).toBe(true) + }) + }) +}) diff --git a/test/npm/object.values.test.mts b/test/npm/object.values.test.mts new file mode 100644 index 00000000..f97e12de --- /dev/null +++ b/test/npm/object.values.test.mts @@ -0,0 +1,91 @@ +/** + * @fileoverview Tests for object.values NPM package override. + * Ported 1:1 from upstream v1.2.0 (7ac272ed): + * https://github.com/es-shims/Object.values/blob/7ac272edeabd13b4ea662e71a2612358b28cfbce/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: values, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + const a = {} + const b = {} + const c = {} + const obj = { a, b, c } + + it('basic support', () => { + expect(values(obj)).toEqual([a, b, c]) + }) + + it('duplicate values are included', () => { + expect(values({ a, b: a, c })).toEqual([a, a, c]) + }) + + it('values are in the same order as keys', () => { + const object: Record = { a, b } + object[0] = 3 + object['c'] = c + object[1] = 4 + delete object[0] + const objKeys = Object.keys(object) + const objValues = objKeys.map(key => object[key]) + expect(values(object)).toEqual(objValues) + }) + + it('non-enumerable properties are omitted', () => { + const object = { a, b } + Object.defineProperty(object, 'c', { enumerable: false, value: c }) + expect(values(object)).toEqual([a, b]) + }) + + it('inherited properties are omitted', () => { + const F = function (this: any) {} as any + F.prototype.a = a + const f = new F() + f.b = b + expect(values(f)).toEqual([b]) + }) + + it('Symbol properties are omitted', () => { + const object: Record = { a, b, c } + const enumSym = Symbol('enum') + const nonEnumSym = Symbol('non enum') + object[enumSym] = enumSym + object['d'] = enumSym + Object.defineProperty(object, nonEnumSym, { + enumerable: false, + value: nonEnumSym, + }) + expect(values(object)).toEqual([a, b, c, enumSym]) + }) + + it('not-yet-visited keys deleted on [[Get]] must not show up in output', () => { + const o: Record = { a: 1, b: 2, c: 3 } + Object.defineProperty(o, 'a', { + get() { + delete this.b + return 1 + }, + }) + expect(values(o)).toEqual([1, 3]) + }) + + it('not-yet-visited keys made non-enumerable on [[Get]] must not show up in output', () => { + const o: Record = { a: 'A', b: 'B' } + Object.defineProperty(o, 'a', { + get() { + Object.defineProperty(o, 'b', { enumerable: false }) + return 'A' + }, + }) + expect(values(o)).toEqual(['A']) + }) +}) diff --git a/test/npm/path-parse.test.mts b/test/npm/path-parse.test.mts new file mode 100644 index 00000000..627e8008 --- /dev/null +++ b/test/npm/path-parse.test.mts @@ -0,0 +1,215 @@ +/** + * @fileoverview Tests for path-parse NPM package override. + * Ported 1:1 from upstream v1.0.7 (f7e258dd): + * https://github.com/jbgutierrez/path-parse/blob/f7e258ddf7c6ec87a0236c75247fc6fd21ee7bd9/test.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: pathParse, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const winParseTests: Array<[Record, string]> = [ + [ + { + root: 'C:\\', + dir: 'C:\\path\\dir', + base: 'index.html', + ext: '.html', + name: 'index', + }, + 'C:\\path\\dir\\index.html', + ], + [ + { + root: 'C:\\', + dir: 'C:\\another_path\\DIR\\1\\2\\33', + base: 'index', + ext: '', + name: 'index', + }, + 'C:\\another_path\\DIR\\1\\2\\33\\index', + ], + [ + { + root: '', + dir: 'another_path\\DIR with spaces\\1\\2\\33', + base: 'index', + ext: '', + name: 'index', + }, + 'another_path\\DIR with spaces\\1\\2\\33\\index', + ], + [{ root: '\\', dir: '\\foo', base: 'C:', ext: '', name: 'C:' }, '\\foo\\C:'], + [{ root: '', dir: '', base: 'file', ext: '', name: 'file' }, 'file'], + [{ root: '', dir: '.', base: 'file', ext: '', name: 'file' }, '.\\file'], + [ + { + root: '\\\\server\\share\\', + dir: '\\\\server\\share\\', + base: 'file_path', + ext: '', + name: 'file_path', + }, + '\\\\server\\share\\file_path', + ], + [ + { + root: '\\\\server two\\shared folder\\', + dir: '\\\\server two\\shared folder\\', + base: 'file path.zip', + ext: '.zip', + name: 'file path', + }, + '\\\\server two\\shared folder\\file path.zip', + ], + [ + { + root: '\\\\teela\\admin$\\', + dir: '\\\\teela\\admin$\\', + base: 'system32', + ext: '', + name: 'system32', + }, + '\\\\teela\\admin$\\system32', + ], + [ + { + root: '\\\\?\\UNC\\', + dir: '\\\\?\\UNC\\server', + base: 'share', + ext: '', + name: 'share', + }, + '\\\\?\\UNC\\server\\share', + ], +] + +const unixParseTests: Array<[Record, string]> = [ + [ + { + root: '/', + dir: '/home/user/dir', + base: 'file.txt', + ext: '.txt', + name: 'file', + }, + '/home/user/dir/file.txt', + ], + [ + { + root: '/', + dir: '/home/user/a dir', + base: 'another File.zip', + ext: '.zip', + name: 'another File', + }, + '/home/user/a dir/another File.zip', + ], + [ + { + root: '/', + dir: '/home/user/a dir/', + base: 'another&File.', + ext: '.', + name: 'another&File', + }, + '/home/user/a dir//another&File.', + ], + [ + { + root: '/', + dir: '/home/user/a$$$dir/', + base: 'another File.zip', + ext: '.zip', + name: 'another File', + }, + '/home/user/a$$$dir//another File.zip', + ], + [ + { + root: '', + dir: 'user/dir', + base: 'another File.zip', + ext: '.zip', + name: 'another File', + }, + 'user/dir/another File.zip', + ], + [{ root: '', dir: '', base: 'file', ext: '', name: 'file' }, 'file'], + [{ root: '', dir: '', base: '.\\file', ext: '', name: '.\\file' }, '.\\file'], + [{ root: '', dir: '.', base: 'file', ext: '', name: 'file' }, './file'], + [{ root: '', dir: '', base: 'C:\\foo', ext: '', name: 'C:\\foo' }, 'C:\\foo'], +] + +const errors = [ + { input: null, message: /Parameter 'pathString' must be a string, not/ }, + { + input: {}, + message: /Parameter 'pathString' must be a string, not object/, + }, + { + input: true, + message: /Parameter 'pathString' must be a string, not boolean/, + }, + { + input: 1, + message: /Parameter 'pathString' must be a string, not number/, + }, + { + input: undefined, + message: /Parameter 'pathString' must be a string, not undefined/, + }, +] + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('win32 parse', () => { + for (const [expected, input] of winParseTests) { + it(`parses ${input}`, () => { + expect(pathParse.win32(input)).toEqual(expected) + }) + } + }) + + describe('posix parse', () => { + for (const [expected, input] of unixParseTests) { + it(`parses ${input}`, () => { + expect(pathParse.posix(input)).toEqual(expected) + }) + } + }) + + describe('win32 errors', () => { + for (const errorCase of errors) { + it(`throws for ${typeof errorCase.input} input`, () => { + try { + pathParse.win32(errorCase.input) + expect.unreachable('should have thrown') + } catch (err: any) { + expect(err).toBeInstanceOf(TypeError) + expect(err.message).toMatch(errorCase.message) + } + }) + } + }) + + describe('posix errors', () => { + for (const errorCase of errors) { + it(`throws for ${typeof errorCase.input} input`, () => { + try { + pathParse.posix(errorCase.input) + expect.unreachable('should have thrown') + } catch (err: any) { + expect(err).toBeInstanceOf(TypeError) + expect(err.message).toMatch(errorCase.message) + } + }) + } + }) +}) diff --git a/test/npm/promise.allsettled.test.mts b/test/npm/promise.allsettled.test.mts new file mode 100644 index 00000000..da153af8 --- /dev/null +++ b/test/npm/promise.allsettled.test.mts @@ -0,0 +1,67 @@ +/** + * @fileoverview Tests for promise.allsettled NPM package override. + * Ported 1:1 from upstream v1.0.7 (3d082d99): + * https://github.com/es-shims/Promise.allSettled/blob/3d082d994f0899a16e40eb5cce901340290581dc/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: allSettled, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const yes = (value: any) => ({ status: 'fulfilled', value }) +const no = (reason: any) => ({ status: 'rejected', reason }) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + const a = {} + const b = {} + const c = {} + + it('no promise values', async () => { + const results = await allSettled([a, b, c]) + expect(results).toEqual([yes(a), yes(b), yes(c)]) + }) + + it('all fulfilled', async () => { + const results = await allSettled([ + Promise.resolve(a), + Promise.resolve(b), + Promise.resolve(c), + ]) + expect(results).toEqual([yes(a), yes(b), yes(c)]) + }) + + it('all rejected', async () => { + const results = await allSettled([ + Promise.reject(a), + Promise.reject(b), + Promise.reject(c), + ]) + expect(results).toEqual([no(a), no(b), no(c)]) + }) + + it('mixed', async () => { + const results = await allSettled([a, Promise.resolve(b), Promise.reject(c)]) + expect(results).toEqual([yes(a), yes(b), no(c)]) + }) + + it('poisoned .then', async () => { + const promise = new Promise(function () {}) + // eslint-disable-next-line unicorn/no-thenable + ;(promise as any).then = function () { + throw new EvalError() + } + try { + await allSettled([promise]) + expect.unreachable('should not reach here') + } catch (reason: any) { + expect(reason instanceof EvalError).toBe(true) + } + }) +}) diff --git a/test/npm/promise.any.test.mts b/test/npm/promise.any.test.mts new file mode 100644 index 00000000..fa9934b1 --- /dev/null +++ b/test/npm/promise.any.test.mts @@ -0,0 +1,85 @@ +/** + * @fileoverview Tests for promise.any NPM package override. + * Ported 1:1 from upstream v2.0.6 (cd47c039): + * https://github.com/es-shims/Promise.any/blob/cd47c0397d3655cd56305cc98a42f8d8fb85f3bb/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: any, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + const a = {} + const b = {} + const c = {} + + it('empty iterable', async () => { + try { + await any([]) + expect.unreachable('should not resolve') + } catch (error: any) { + expect(error instanceof AggregateError).toBe(true) + expect(error.errors).toEqual([]) + } + }) + + it('no promise values', async () => { + const result = await any([a, b, c]) + expect(result).toEqual(a) + }) + + it('all fulfilled', async () => { + const result = await any([ + Promise.resolve(a), + Promise.resolve(b), + Promise.resolve(c), + ]) + expect(result).toEqual(a) + }) + + it('all rejected', async () => { + try { + await any([Promise.reject(a), Promise.reject(b), Promise.reject(c)]) + expect.unreachable('should not resolve') + } catch (error: any) { + expect(error instanceof AggregateError).toBe(true) + expect(error.errors).toEqual([a, b, c]) + } + }) + + it('mixed - first non-promise wins', async () => { + const result = await any([a, Promise.resolve(b), Promise.reject(c)]) + expect(result).toEqual(a) + }) + + it('mixed - first resolved wins', async () => { + const result = await any([ + Promise.reject(a), + Promise.resolve(b), + Promise.reject(c), + ]) + expect(result).toEqual(b) + }) + + it('poisoned .then', async () => { + const poison = new EvalError() + const promise = new Promise(function () {}) + // eslint-disable-next-line unicorn/no-thenable + ;(promise as any).then = function () { + throw poison + } + try { + await any([promise]) + expect.unreachable('should not reach here') + } catch (error: any) { + expect(error).toBe(poison) + } + }) +}) diff --git a/test/npm/querystringify.test.mts b/test/npm/querystringify.test.mts new file mode 100644 index 00000000..1594f8dc --- /dev/null +++ b/test/npm/querystringify.test.mts @@ -0,0 +1,131 @@ +/** + * @fileoverview Tests for querystringify NPM package override. + * Ported 1:1 from upstream v2.2.0 (73db95a5): + * https://github.com/unshiftio/querystringify/blob/73db95a504f988dce3f790e174e298ceb2b46a8e/test.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: qs, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('#stringify', () => { + const obj = { foo: 'bar', bar: 'foo' } + + it('is exposed as method', () => { + expect(typeof qs.stringify).toBe('function') + }) + + it('transforms an object', () => { + expect(qs.stringify(obj)).toBe('foo=bar&bar=foo') + }) + + it('can optionally prefix', () => { + expect(qs.stringify(obj, true)).toBe('?foo=bar&bar=foo') + }) + + it('can prefix with custom things', () => { + expect(qs.stringify(obj, '&')).toBe('&foo=bar&bar=foo') + }) + + it('doesnt prefix empty query strings', () => { + expect(qs.stringify({}, true)).toBe('') + expect(qs.stringify({})).toBe('') + }) + + it('works with object keys with empty string values', () => { + expect(qs.stringify({ foo: '' })).toBe('foo=') + }) + + it('works with nulled objects', () => { + const o = Object.create(null) + o.foo = 'bar' + expect(qs.stringify(o)).toBe('foo=bar') + }) + + it('transforms undefined into nothing', () => { + expect(qs.stringify({ foo: undefined })).toBe('foo=') + }) + + it('transforms NaN into nothing', () => { + expect(qs.stringify({ foo: NaN })).toBe('foo=') + }) + + it('transforms null into nothing', () => { + expect(qs.stringify({ foo: null })).toBe('foo=') + }) + }) + + describe('#parse', () => { + it('is exposed as method', () => { + expect(typeof qs.parse).toBe('function') + }) + + it('will parse a querystring to an object', () => { + const obj = qs.parse('foo=bar') + expect(typeof obj).toBe('object') + expect(obj.foo).toBe('bar') + }) + + it('will also work if querystring is prefixed with ?', () => { + const obj = qs.parse('?foo=bar&shizzle=mynizzle') + expect(typeof obj).toBe('object') + expect(obj.foo).toBe('bar') + expect(obj.shizzle).toBe('mynizzle') + }) + + it('will also work if querystring is prefixed with #', () => { + const obj = qs.parse('#foo=bar&shizzle=mynizzle') + expect(typeof obj).toBe('object') + expect(obj.foo).toBe('bar') + expect(obj.shizzle).toBe('mynizzle') + }) + + it('does not override prototypes', () => { + const obj = qs.parse('?toString&__proto__=lol') + expect(typeof obj).toBe('object') + expect(typeof obj.toString).toBe('function') + expect(obj.__proto__).not.toBe('lol') + }) + + it('works with querystring parameters without values', () => { + const obj = qs.parse('?foo&bar=&shizzle=mynizzle') + expect(typeof obj).toBe('object') + expect(obj.foo).toBe('') + expect(obj.bar).toBe('') + expect(obj.shizzle).toBe('mynizzle') + }) + + it('first value wins', () => { + const obj = qs.parse('foo=1&foo=2') + expect(obj.foo).toBe('1') + }) + + it('decodes plus signs', () => { + let obj = qs.parse('foo+bar=baz+qux') + expect(typeof obj).toBe('object') + expect(obj['foo bar']).toBe('baz qux') + + obj = qs.parse('foo+bar=baz%2Bqux') + expect(typeof obj).toBe('object') + expect(obj['foo bar']).toBe('baz+qux') + }) + + it('does not throw on invalid input', () => { + expect(() => qs.parse('?%&')).not.toThrow() + }) + + it('does not include invalid output', () => { + const obj = qs.parse('?%&') + expect(typeof obj).toBe('object') + expect(Object.keys(obj).length).toBe(0) + }) + }) +}) diff --git a/test/npm/reflect.getprototypeof.test.mts b/test/npm/reflect.getprototypeof.test.mts new file mode 100644 index 00000000..115a3ac7 --- /dev/null +++ b/test/npm/reflect.getprototypeof.test.mts @@ -0,0 +1,56 @@ +/** + * @fileoverview Tests for reflect.getprototypeof NPM package override. + * Ported 1:1 from upstream v1.0.10 (00db5482): + * https://github.com/es-shims/Reflect.getPrototypeOf/blob/00db548275a0c1bb078d31679c3f4a0fc0ccdf58/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: getPrototypeOf, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws for undefined', () => { + expect(() => getPrototypeOf(undefined)).toThrow(TypeError) + }) + + it('throws for null', () => { + expect(() => getPrototypeOf(null)).toThrow(TypeError) + }) + + it('throws for primitives', () => { + expect(() => getPrototypeOf(true)).toThrow() + expect(() => getPrototypeOf(false)).toThrow() + expect(() => getPrototypeOf(42)).toThrow() + expect(() => getPrototypeOf(NaN)).toThrow() + expect(() => getPrototypeOf(0)).toThrow() + expect(() => getPrototypeOf(-0)).toThrow() + expect(() => getPrototypeOf(Infinity)).toThrow() + expect(() => getPrototypeOf(-Infinity)).toThrow() + expect(() => getPrototypeOf('')).toThrow() + expect(() => getPrototypeOf('foo')).toThrow() + }) + + it('returns correct prototypes for objects', () => { + expect(getPrototypeOf(/a/g)).toBe(RegExp.prototype) + expect(getPrototypeOf(new Date())).toBe(Date.prototype) + expect(getPrototypeOf(function () {})).toBe(Function.prototype) + expect(getPrototypeOf([])).toBe(Array.prototype) + expect(getPrototypeOf({})).toBe(Object.prototype) + }) + + it('handles null prototype objects', () => { + const obj = { __proto__: null } + if ('toString' in obj) { + expect(getPrototypeOf(obj)).toBe(Object.prototype) + } else { + expect(getPrototypeOf(obj)).toBe(null) + } + }) +}) diff --git a/test/npm/reflect.ownkeys.test.mts b/test/npm/reflect.ownkeys.test.mts new file mode 100644 index 00000000..fb0c4b6d --- /dev/null +++ b/test/npm/reflect.ownkeys.test.mts @@ -0,0 +1,78 @@ +/** + * @fileoverview Tests for reflect.ownkeys NPM package override. + * Ported 1:1 from upstream v1.1.6 (0fb9ad70): + * https://github.com/es-shims/Reflect.ownKeys/blob/0fb9ad709d66d2b4fe8d113028f00a6d21eb1fbc/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: ownKeys, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('normal object', () => { + const o = { a: 1, b: 2 } + expect(ownKeys(o).sort()).toEqual(['a', 'b'].sort()) + }) + + it('object with prototype', () => { + const p = { a: 1, b: 2 } + const o = { c: 3, d: 4, __proto__: p } + expect(ownKeys(o).sort()).toEqual(['c', 'd'].sort()) + }) + + it('object with non-enumerable properties', () => { + const o: Record = {} + Object.defineProperty(o, 'a', { + enumerable: false, + value: 1, + }) + Object.defineProperty(o, 'b', { + get() { + return 2 + }, + enumerable: false, + }) + expect(ownKeys(o).sort()).toEqual(['a', 'b'].sort()) + }) + + describe('Symbols', () => { + it('object with own symbol properties gets own keys', () => { + const a = Symbol('a') + const b = Symbol('b') + + const o: Record = { a: 1, b: 2 } + o[a] = 3 + o[b] = 4 + expect(ownKeys(o)).toEqual(['a', 'b', a, b]) + }) + + it('object with symbol properties in prototype gets own keys', () => { + const a = Symbol('a') + const b = Symbol('b') + + const p: Record = { a: 1 } + p[a] = 3 + const child: Record = { __proto__: p } + child['b'] = 2 + child[b] = 4 + expect(ownKeys(child)).toEqual(['b', b]) + }) + + it('object with non-enumerable symbol properties', () => { + const a = Symbol('a') + const nonEnum: Record = { a: 1 } + Object.defineProperty(nonEnum, a, { + enumerable: false, + value: 1, + }) + expect(ownKeys(nonEnum)).toEqual(['a', a]) + }) + }) +}) diff --git a/test/npm/regexp.prototype.flags.test.mts b/test/npm/regexp.prototype.flags.test.mts new file mode 100644 index 00000000..a295baf6 --- /dev/null +++ b/test/npm/regexp.prototype.flags.test.mts @@ -0,0 +1,123 @@ +/** + * @fileoverview Tests for regexp.prototype.flags NPM package override. + * Ported 1:1 from upstream v1.5.4 (8e2eeaab): + * https://github.com/es-shims/RegExp.prototype.flags/blob/8e2eeaabb66e005d34c9508ab6d83456ff1d4010/test/tests.js + */ + +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: flags, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const hasOwn = (obj: any, key: string) => + Object.prototype.hasOwnProperty.call(obj, key) + +const getRegexLiteral = (stringRegex: string) => { + try { + // eslint-disable-next-line no-new-func + return Function('return ' + stringRegex + ';')() + } catch (_e) { + return undefined + } +} + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws when called with a non-object receiver', () => { + const primitives = [ + undefined, + null, + false, + true, + 0, + -0, + NaN, + 42, + Infinity, + -Infinity, + '', + 'foo', + ] + for (const nonObject of primitives) { + expect(() => flags(nonObject)).toThrow(TypeError) + } + }) + + it('basic flag extraction', () => { + expect(flags(/a/g)).toBe('g') + expect(flags(/a/gim)).toBe('gim') + expect(flags(new RegExp('a', 'gmi'))).toBe('gim') + expect(flags(/a/)).toBe('') + expect(flags(new RegExp('a'))).toBe('') + }) + + it('sorting', () => { + expect(flags(/a/gim)).toBe('gim') + expect(flags(/a/gim)).toBe('gim') + expect(flags(/a/gim)).toBe('gim') + if (hasOwn(RegExp.prototype, 'sticky')) { + expect(flags(getRegexLiteral('/a/gyim'))).toBe('gimy') + } + if (hasOwn(RegExp.prototype, 'unicode')) { + expect(flags(getRegexLiteral('/a/ugmi'))).toBe('gimu') + } + if (hasOwn(RegExp.prototype, 'dotAll')) { + expect(flags(getRegexLiteral('/a/sgmi'))).toBe('gims') + } + }) + + it('generic flags', () => { + expect(flags({})).toBe('') + expect(flags({ ignoreCase: true })).toBe('i') + expect(flags({ dotAll: 1, global: 0, sticky: 1, unicode: 1 })).toBe('suy') + expect(flags({ __proto__: { multiline: true } })).toBe('m') + }) + + it('getters are called in expected order', () => { + let calls = '' + const re: Record = {} + Object.defineProperty(re, 'hasIndices', { + get() { + calls += 'd' + }, + }) + Object.defineProperty(re, 'global', { + get() { + calls += 'g' + }, + }) + Object.defineProperty(re, 'ignoreCase', { + get() { + calls += 'i' + }, + }) + Object.defineProperty(re, 'multiline', { + get() { + calls += 'm' + }, + }) + Object.defineProperty(re, 'dotAll', { + get() { + calls += 's' + }, + }) + Object.defineProperty(re, 'unicode', { + get() { + calls += 'u' + }, + }) + Object.defineProperty(re, 'sticky', { + get() { + calls += 'y' + }, + }) + + flags(re) + expect(calls).toBe('dgimsuy') + }) +}) diff --git a/test/npm/safe-array-concat.test.mts b/test/npm/safe-array-concat.test.mts new file mode 100644 index 00000000..e5fb4aea --- /dev/null +++ b/test/npm/safe-array-concat.test.mts @@ -0,0 +1,60 @@ +/** + * @fileoverview Tests for safe-array-concat NPM package override. + * Ported 1:1 from upstream v1.1.3 (eff5359f): + * https://github.com/ljharb/safe-array-concat/blob/eff5359f/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: safeConcat, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof safeConcat).toBe('function') + }) + + it('works with flat and nested arrays', () => { + expect(safeConcat([1, 2], [3, 4], 'foo', 5, 6, [[7]])).toEqual([ + 1, + 2, + 3, + 4, + 'foo', + 5, + 6, + [7], + ]) + }) + + it('first item as undefined is not the concat receiver', () => { + expect(safeConcat(undefined, 1, 2)).toEqual([undefined, 1, 2]) + }) + + it('first item as null is not the concat receiver', () => { + expect(safeConcat(null, 1, 2)).toEqual([null, 1, 2]) + }) + + it('ignores nonArray .constructor on first item', () => { + const arr: any = [1, 2] + arr.constructor = function C() { + return { args: arguments } + } + expect(safeConcat(arr, 3, 4)).toEqual([1, 2, 3, 4]) + }) + + it('ignores Symbol.species on first item', () => { + const species = Symbol.species + const speciesArr: any = [1, 2] + speciesArr.constructor = {} + speciesArr.constructor[species] = function Species() { + return { args: arguments } + } + expect(safeConcat(speciesArr, 3, 4)).toEqual([1, 2, 3, 4]) + }) +}) diff --git a/test/npm/safe-buffer.test.mts b/test/npm/safe-buffer.test.mts new file mode 100644 index 00000000..66a49724 --- /dev/null +++ b/test/npm/safe-buffer.test.mts @@ -0,0 +1,89 @@ +/** + * @fileoverview Tests for safe-buffer NPM package override. + * Ported 1:1 from upstream v5.2.1 (c937d657): + * https://github.com/feross/safe-buffer/blob/c937d657/test/basic.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: safeBufferModule, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const SafeBuffer = safeBufferModule?.Buffer + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('new SafeBuffer(value) works just like Buffer', () => { + expect(new SafeBuffer('hey')).toEqual(Buffer.from('hey')) + expect(new SafeBuffer('hey', 'utf8')).toEqual(Buffer.from('hey', 'utf8')) + expect(new SafeBuffer('686579', 'hex')).toEqual( + Buffer.from('686579', 'hex'), + ) + expect(new SafeBuffer([1, 2, 3])).toEqual(Buffer.from([1, 2, 3])) + expect(new SafeBuffer(new Uint8Array([1, 2, 3]))).toEqual( + Buffer.from(new Uint8Array([1, 2, 3])), + ) + + expect(typeof SafeBuffer.isBuffer).toBe('function') + expect(SafeBuffer.isBuffer(new SafeBuffer('hey'))).toBe(true) + expect(Buffer.isBuffer(new SafeBuffer('hey'))).toBe(true) + expect(SafeBuffer.isBuffer({})).toBe(false) + }) + + it('SafeBuffer.from(value) converts to a Buffer', () => { + expect(SafeBuffer.from('hey')).toEqual(Buffer.from('hey')) + expect(SafeBuffer.from('hey', 'utf8')).toEqual(Buffer.from('hey', 'utf8')) + expect(SafeBuffer.from('686579', 'hex')).toEqual( + Buffer.from('686579', 'hex'), + ) + expect(SafeBuffer.from([1, 2, 3])).toEqual(Buffer.from([1, 2, 3])) + expect(SafeBuffer.from(new Uint8Array([1, 2, 3]))).toEqual( + Buffer.from(new Uint8Array([1, 2, 3])), + ) + }) + + it('SafeBuffer.alloc(number) returns zeroed-out memory', () => { + for (let i = 0; i < 10; i++) { + const buf1 = SafeBuffer.alloc(1000) + expect(buf1.length).toBe(1000) + expect(buf1.every((b: number) => b === 0)).toBe(true) + } + + const buf2 = SafeBuffer.alloc(1000 * 1000) + expect(buf2.length).toBe(1000 * 1000) + expect(buf2.every((b: number) => b === 0)).toBe(true) + }) + + it('SafeBuffer.allocUnsafe(number)', () => { + const buf = SafeBuffer.allocUnsafe(100) + expect(buf.length).toBe(100) + expect(SafeBuffer.isBuffer(buf)).toBe(true) + expect(Buffer.isBuffer(buf)).toBe(true) + }) + + it('SafeBuffer.from() throws with number types', () => { + expect(() => SafeBuffer.from(0)).toThrow() + expect(() => SafeBuffer.from(-1)).toThrow() + expect(() => SafeBuffer.from(NaN)).toThrow() + expect(() => SafeBuffer.from(Infinity)).toThrow() + expect(() => SafeBuffer.from(99)).toThrow() + }) + + it('SafeBuffer.allocUnsafe() throws with non-number types', () => { + expect(() => SafeBuffer.allocUnsafe('hey')).toThrow() + expect(() => SafeBuffer.allocUnsafe('hey', 'utf8')).toThrow() + expect(() => SafeBuffer.allocUnsafe([1, 2, 3])).toThrow() + expect(() => SafeBuffer.allocUnsafe({})).toThrow() + }) + + it('SafeBuffer.alloc() throws with non-number types', () => { + expect(() => SafeBuffer.alloc('hey')).toThrow() + expect(() => SafeBuffer.alloc('hey', 'utf8')).toThrow() + expect(() => SafeBuffer.alloc([1, 2, 3])).toThrow() + expect(() => SafeBuffer.alloc({})).toThrow() + }) +}) diff --git a/test/npm/safe-regex-test.test.mts b/test/npm/safe-regex-test.test.mts new file mode 100644 index 00000000..6600cdaa --- /dev/null +++ b/test/npm/safe-regex-test.test.mts @@ -0,0 +1,50 @@ +/** + * @fileoverview Tests for safe-regex-test NPM package override. + * Ported 1:1 from upstream v1.1.0 (9360cf07): + * https://github.com/ljharb/safe-regex-test/blob/9360cf07/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: regexTester, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof regexTester).toBe('function') + }) + + it('throws on non-regexes', () => { + const nonRegexes = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + [], + {}, + () => {}, + ] + for (const val of nonRegexes) { + expect(() => regexTester(val)).toThrow(TypeError) + } + }) + + it('returns a tester function for regexes', () => { + const tester = regexTester(/a/) + expect(typeof tester).toBe('function') + expect(tester('a')).toBe(true) + expect(tester('b')).toBe(false) + expect(tester('a')).toBe(true) + }) +}) diff --git a/test/npm/set-function-length.test.mts b/test/npm/set-function-length.test.mts new file mode 100644 index 00000000..b0d01d57 --- /dev/null +++ b/test/npm/set-function-length.test.mts @@ -0,0 +1,75 @@ +/** + * @fileoverview Tests for set-function-length NPM package override. + * Ported 1:1 from upstream v1.2.2 (2290d3ea): + * https://github.com/ljharb/set-function-length/blob/2290d3ea/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: setFunctionLength, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws on non-functions', () => { + const nonFunctions = [ + undefined, + null, + true, + false, + 0, + 42, + '', + 'foo', + /a/g, + [], + {}, + ] + for (const nonFn of nonFunctions) { + expect(() => setFunctionLength(nonFn)).toThrow(TypeError) + } + }) + + it('throws on non-integer lengths', () => { + const nonIntegers = [ + undefined, + null, + true, + false, + '', + 'foo', + /a/g, + [], + {}, + 1.5, + 0xffffffff + 1, + ] + for (const nonInt of nonIntegers) { + expect(() => setFunctionLength(function () {}, nonInt)).toThrow(TypeError) + } + }) + + it('sets the length of a function', () => { + const fns = [ + function zero() {}, + function one(_x: any) {}, + function two(_x: any, _y: any) {}, + ] + for (const fn of fns) { + const origLength = fn.length + const newLength = origLength * 2 + 12 + expect(setFunctionLength(fn, newLength)).toBe(fn) + expect(fn.length).toBe(newLength) + } + }) + + it('sets the length loosely', () => { + const fn = function zero() {} + expect(setFunctionLength(fn, 42, true)).toBe(fn) + expect(fn.length).toBe(42) + }) +}) diff --git a/test/npm/side-channel.test.mts b/test/npm/side-channel.test.mts new file mode 100644 index 00000000..d03dec31 --- /dev/null +++ b/test/npm/side-channel.test.mts @@ -0,0 +1,117 @@ +/** + * @fileoverview Tests for side-channel NPM package override. + * Ported 1:1 from upstream v1.1.0 (26e368c3): + * https://github.com/ljharb/side-channel/blob/26e368c3/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: getSideChannel, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function that returns a channel object', () => { + expect(typeof getSideChannel).toBe('function') + expect(getSideChannel.length).toBe(0) + const channel = getSideChannel() + expect(channel).toBeTruthy() + expect(typeof channel).toBe('object') + }) + + describe('assert', () => { + it('throws for nonexistent value', () => { + const channel = getSideChannel() + expect(() => channel.assert({})).toThrow(TypeError) + }) + + it('does not throw for existent value', () => { + const channel = getSideChannel() + const o = {} + channel.set(o, 'data') + expect(() => channel.assert(o)).not.toThrow() + }) + }) + + describe('has', () => { + it('returns false for nonexistent value', () => { + const channel = getSideChannel() + const o: unknown[] = [] + expect(channel.has(o)).toBe(false) + }) + + it('returns true for existent value', () => { + const channel = getSideChannel() + const o: unknown[] = [] + channel.set(o, 'foo') + expect(channel.has(o)).toBe(true) + }) + + it('works with non-object keys', () => { + const channel = getSideChannel() + expect(channel.has('abc')).toBe(false) + channel.set('abc', 'foo') + expect(channel.has('abc')).toBe(true) + }) + }) + + describe('get', () => { + it('returns undefined for nonexistent value', () => { + const channel = getSideChannel() + const o = {} + expect(channel.get(o)).toBe(undefined) + }) + + it('returns data set by set', () => { + const channel = getSideChannel() + const o = {} + const data = {} + channel.set(o, data) + expect(channel.get(o)).toBe(data) + }) + }) + + describe('set', () => { + it('sets and updates values', () => { + const channel = getSideChannel() + const o = function () {} + expect(channel.get(o)).toBe(undefined) + + channel.set(o, 42) + expect(channel.get(o)).toBe(42) + + channel.set(o, Infinity) + expect(channel.get(o)).toBe(Infinity) + + const o2 = {} + channel.set(o2, 17) + expect(channel.get(o)).toBe(Infinity) + expect(channel.get(o2)).toBe(17) + + channel.set(o, 14) + expect(channel.get(o)).toBe(14) + expect(channel.get(o2)).toBe(17) + }) + }) + + describe('delete', () => { + it('returns false for nonexistent value', () => { + const channel = getSideChannel() + expect(channel.delete({})).toBe(false) + }) + + it('deletes existent value', () => { + const channel = getSideChannel() + const o = {} + channel.set(o, 42) + expect(channel.has(o)).toBe(true) + expect(channel.delete({})).toBe(false) + expect(channel.delete(o)).toBe(true) + expect(channel.has(o)).toBe(false) + }) + }) +}) diff --git a/test/npm/string.fromcodepoint.test.mts b/test/npm/string.fromcodepoint.test.mts new file mode 100644 index 00000000..2fbdb8bf --- /dev/null +++ b/test/npm/string.fromcodepoint.test.mts @@ -0,0 +1,92 @@ +/** + * @fileoverview Tests for string.fromcodepoint NPM package override. + * Ported 1:1 from upstream v1.0.3 (e3cfae0b): + * https://github.com/mathiasbynens/String.fromCodePoint/blob/e3cfae0b/tests/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: fromCodePoint, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('returns empty string with no arguments', () => { + expect(fromCodePoint()).toBe('') + }) + + describe('cast to 0', () => { + it('converts falsy values to \\0', () => { + expect(fromCodePoint('')).toBe('\0') + expect(fromCodePoint(-0)).toBe('\0') + expect(fromCodePoint(0)).toBe('\0') + expect(fromCodePoint(false)).toBe('\0') + expect(fromCodePoint(null)).toBe('\0') + }) + }) + + describe('astral code points', () => { + it('handles astral code points', () => { + expect(fromCodePoint(0x1d306)).toBe('\uD834\uDF06') + expect(fromCodePoint(0x1d306, 0x61, 0x1d307)).toBe( + '\uD834\uDF06a\uD834\uDF07', + ) + expect(fromCodePoint(0x61, 0x62, 0x1d307)).toBe('ab\uD834\uDF07') + }) + }) + + describe('invalid code points', () => { + it('throws RangeError for invalid values', () => { + expect(() => fromCodePoint('_')).toThrow(RangeError) + expect(() => fromCodePoint('+Infinity')).toThrow(RangeError) + expect(() => fromCodePoint('-Infinity')).toThrow(RangeError) + expect(() => fromCodePoint(-1)).toThrow(RangeError) + expect(() => fromCodePoint(0x10ffff + 1)).toThrow(RangeError) + expect(() => fromCodePoint(3.14)).toThrow(RangeError) + expect(() => fromCodePoint(3e-2)).toThrow(RangeError) + expect(() => fromCodePoint(-Infinity)).toThrow(RangeError) + expect(() => fromCodePoint(Number(Infinity))).toThrow(RangeError) + expect(() => fromCodePoint(NaN)).toThrow(RangeError) + expect(() => fromCodePoint(undefined)).toThrow(RangeError) + expect(() => fromCodePoint({})).toThrow(RangeError) + expect(() => fromCodePoint(/./)).toThrow(RangeError) + }) + }) + + describe('cast code point', () => { + it('uses valueOf on objects', () => { + let tmp = 0x60 + expect( + fromCodePoint({ + valueOf: function () { + ++tmp + return tmp + }, + }), + ).toBe('a') + expect(tmp).toBe(0x61) + }) + }) + + describe('long arguments list', () => { + it('does not throw for large argument lists', () => { + let counter = (Math.pow(2, 15) * 3) / 2 + let result: number[] = [] + while (--counter >= 0) { + result.push(0) + } + expect(() => fromCodePoint.apply(null, result)).not.toThrow() + + counter = (Math.pow(2, 15) * 3) / 2 + result = [] + while (--counter >= 0) { + result.push(0xffff + 1) + } + expect(() => fromCodePoint.apply(null, result)).not.toThrow() + }) + }) +}) diff --git a/test/npm/string.prototype.at.test.mts b/test/npm/string.prototype.at.test.mts new file mode 100644 index 00000000..289c3fa1 --- /dev/null +++ b/test/npm/string.prototype.at.test.mts @@ -0,0 +1,38 @@ +/** + * @fileoverview Tests for string.prototype.at NPM package override. + * Ported 1:1 from upstream v1.0.6 (515b26f9): + * https://github.com/es-shims/String.prototype.at/blob/515b26f9/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: at, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('strings', () => { + it('returns character at positive index', () => { + expect(at('abc', 0)).toBe('a') + expect(at('abc', 1)).toBe('b') + expect(at('abc', 2)).toBe('c') + }) + + it('returns character at negative index', () => { + expect(at('abc', -3)).toBe('a') + expect(at('abc', -2)).toBe('b') + expect(at('abc', -1)).toBe('c') + }) + + it('returns undefined for out-of-bounds index', () => { + expect(at('abc', 3)).toBe(undefined) + expect(at('abc', -4)).toBe(undefined) + expect(at('abc', Infinity)).toBe(undefined) + expect(at('abc', -Infinity)).toBe(undefined) + }) + }) +}) diff --git a/test/npm/string.prototype.codepointat.test.mts b/test/npm/string.prototype.codepointat.test.mts new file mode 100644 index 00000000..e505cb71 --- /dev/null +++ b/test/npm/string.prototype.codepointat.test.mts @@ -0,0 +1,98 @@ +/** + * @fileoverview Tests for string.prototype.codepointat NPM package override. + * Ported 1:1 from upstream v1.0.1 (4dd4742e): + * https://github.com/mathiasbynens/String.prototype.codePointAt/blob/4dd4742e/tests/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: codePointAt, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('String that starts with a BMP symbol', () => { + it('returns correct code points', () => { + expect(codePointAt('abc\uD834\uDF06def', -1)).toBe(undefined) + expect(codePointAt('abc\uD834\uDF06def', -0)).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', 0)).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', 3)).toBe(0x1d306) + expect(codePointAt('abc\uD834\uDF06def', 4)).toBe(0xdf06) + expect(codePointAt('abc\uD834\uDF06def', 5)).toBe(0x64) + expect(codePointAt('abc\uD834\uDF06def', 42)).toBe(undefined) + }) + }) + + describe('String that starts with a BMP symbol - cast position', () => { + it('casts position argument', () => { + expect(codePointAt('abc\uD834\uDF06def', '')).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', '_')).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def')).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', -Infinity)).toBe(undefined) + expect(codePointAt('abc\uD834\uDF06def', Infinity)).toBe(undefined) + expect(codePointAt('abc\uD834\uDF06def', NaN)).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', false)).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', null)).toBe(0x61) + expect(codePointAt('abc\uD834\uDF06def', undefined)).toBe(0x61) + }) + }) + + describe('String that starts with an astral symbol', () => { + it('returns correct code points', () => { + expect(codePointAt('\uD834\uDF06def', -1)).toBe(undefined) + expect(codePointAt('\uD834\uDF06def', -0)).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', 0)).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', 1)).toBe(0xdf06) + expect(codePointAt('\uD834\uDF06def', 42)).toBe(undefined) + }) + }) + + describe('String that starts with an astral symbol - cast position', () => { + it('casts position argument', () => { + expect(codePointAt('\uD834\uDF06def', '')).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', '1')).toBe(0xdf06) + expect(codePointAt('\uD834\uDF06def', '_')).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def')).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', false)).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', null)).toBe(0x1d306) + expect(codePointAt('\uD834\uDF06def', undefined)).toBe(0x1d306) + }) + }) + + describe('Lone high surrogates', () => { + it('returns surrogate code point', () => { + expect(codePointAt('\uD834abc', -1)).toBe(undefined) + expect(codePointAt('\uD834abc', -0)).toBe(0xd834) + expect(codePointAt('\uD834abc', 0)).toBe(0xd834) + }) + }) + + describe('Lone low surrogates', () => { + it('returns surrogate code point', () => { + expect(codePointAt('\uDF06abc', -1)).toBe(undefined) + expect(codePointAt('\uDF06abc', -0)).toBe(0xdf06) + expect(codePointAt('\uDF06abc', 0)).toBe(0xdf06) + }) + }) + + describe('cast this value', () => { + it('converts non-strings via ToString', () => { + expect(codePointAt(42, 0)).toBe(0x34) + expect(codePointAt(42, 1)).toBe(0x32) + expect( + codePointAt( + { + toString: function () { + return 'abc' + }, + }, + 2, + ), + ).toBe(0x63) + }) + }) +}) diff --git a/test/npm/string.prototype.endswith.test.mts b/test/npm/string.prototype.endswith.test.mts new file mode 100644 index 00000000..7518d74e --- /dev/null +++ b/test/npm/string.prototype.endswith.test.mts @@ -0,0 +1,121 @@ +/** + * @fileoverview Tests for string.prototype.endswith NPM package override. + * Ported 1:1 from upstream v1.0.2 (3803a49d): + * https://github.com/mathiasbynens/String.prototype.endsWith/blob/3803a49d/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: endsWith, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('nullish search string', () => { + it('handles undefined and null', () => { + expect(endsWith('undefined')).toBe(true) + expect(endsWith('undefined', undefined)).toBe(true) + expect(endsWith('undefined', null)).toBe(false) + expect(endsWith('null')).toBe(false) + expect(endsWith('null', undefined)).toBe(false) + expect(endsWith('null', null)).toBe(true) + }) + }) + + describe('basic support', () => { + it('checks string endings', () => { + expect(endsWith('abc')).toBe(false) + expect(endsWith('abc', '')).toBe(true) + expect(endsWith('abc', '\0')).toBe(false) + expect(endsWith('abc', 'c')).toBe(true) + expect(endsWith('abc', 'b')).toBe(false) + expect(endsWith('abc', 'ab')).toBe(false) + expect(endsWith('abc', 'bc')).toBe(true) + expect(endsWith('abc', 'abc')).toBe(true) + expect(endsWith('abc', 'bcd')).toBe(false) + expect(endsWith('abc', 'abcd')).toBe(false) + expect(endsWith('abc', 'bcde')).toBe(false) + }) + }) + + describe('position argument - NaN', () => { + it('treats NaN as 0', () => { + expect(endsWith('abc', '', NaN)).toBe(true) + expect(endsWith('abc', '\0', NaN)).toBe(false) + expect(endsWith('abc', 'c', NaN)).toBe(false) + expect(endsWith('abc', 'b', NaN)).toBe(false) + expect(endsWith('abc', 'a', NaN)).toBe(false) + expect(endsWith('abc', 'abc', NaN)).toBe(false) + }) + }) + + describe('position argument - 1', () => { + it('limits search to position 1', () => { + expect(endsWith('abc', '', 1)).toBe(true) + expect(endsWith('abc', 'a', 1)).toBe(true) + expect(endsWith('abc', 'b', 1)).toBe(false) + expect(endsWith('abc', 'c', 1)).toBe(false) + expect(endsWith('abc', 'abc', 1)).toBe(false) + }) + }) + + describe('position argument - 2', () => { + it('limits search to position 2', () => { + expect(endsWith('abc', '', 2)).toBe(true) + expect(endsWith('abc', 'b', 2)).toBe(true) + expect(endsWith('abc', 'ab', 2)).toBe(true) + expect(endsWith('abc', 'c', 2)).toBe(false) + expect(endsWith('abc', 'abc', 2)).toBe(false) + }) + }) + + describe('position argument - +Infinity', () => { + it('treats Infinity as full length', () => { + expect(endsWith('abc', '', Number(Infinity))).toBe(true) + expect(endsWith('abc', 'c', Number(Infinity))).toBe(true) + expect(endsWith('abc', 'bc', Number(Infinity))).toBe(true) + expect(endsWith('abc', 'abc', Number(Infinity))).toBe(true) + expect(endsWith('abc', 'bcd', Number(Infinity))).toBe(false) + }) + }) + + describe('search regexp', () => { + it('throws TypeError for regex search string', () => { + expect(endsWith('[a-z]+(bar)?', '(bar)?')).toBe(true) + expect(() => endsWith('[a-z]+(bar)?', /(bar)?/)).toThrow(TypeError) + expect(endsWith('[a-z]+(bar)?', '[a-z]+', 6)).toBe(true) + }) + }) + + describe('nullish this object', () => { + it('throws TypeError for null/undefined this', () => { + expect(() => endsWith(undefined)).toThrow(TypeError) + expect(() => endsWith(undefined, 'b')).toThrow(TypeError) + expect(() => endsWith(null)).toThrow(TypeError) + expect(() => endsWith(null, 'b')).toThrow(TypeError) + }) + }) + + describe('cast this object', () => { + it('converts this via ToString', () => { + expect(endsWith(42, '2')).toBe(true) + expect(endsWith(42, '4')).toBe(false) + expect(endsWith(42, '2', 4)).toBe(true) + expect( + endsWith( + { + toString: function () { + return 'abc' + }, + }, + 'b', + 2, + ), + ).toBe(true) + }) + }) +}) diff --git a/test/npm/string.prototype.includes.test.mts b/test/npm/string.prototype.includes.test.mts new file mode 100644 index 00000000..46f83b50 --- /dev/null +++ b/test/npm/string.prototype.includes.test.mts @@ -0,0 +1,98 @@ +/** + * @fileoverview Tests for string.prototype.includes NPM package override. + * Ported 1:1 from upstream v2.0.1 (4dd89acb): + * https://github.com/mathiasbynens/String.prototype.includes/blob/4dd89acb/tests/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: includes, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('cast searchString arg', () => { + it('handles various types', () => { + expect(includes('abc')).toBe(false) + expect(includes('aundefinedb')).toBe(true) + expect(includes('abc', undefined)).toBe(false) + expect(includes('aundefinedb', undefined)).toBe(true) + expect(includes('abc', null)).toBe(false) + expect(includes('anullb', null)).toBe(true) + expect(includes('abc', false)).toBe(false) + expect(includes('afalseb', false)).toBe(true) + expect(includes('abc', NaN)).toBe(false) + expect(includes('aNaNb', NaN)).toBe(true) + }) + }) + + describe('basic support', () => { + it('checks string inclusion', () => { + expect(includes('abc', 'abc')).toBe(true) + expect(includes('abc', 'def')).toBe(false) + expect(includes('abc', '')).toBe(true) + expect(includes('', '')).toBe(true) + expect(includes('abc', 'bc')).toBe(true) + expect(includes('abc', 'bc\0')).toBe(false) + }) + }) + + describe('pos argument', () => { + it('respects position', () => { + expect(includes('abc', 'b', -Infinity)).toBe(true) + expect(includes('abc', 'b', -1)).toBe(true) + expect(includes('abc', 'b', -0)).toBe(true) + expect(includes('abc', 'b', +0)).toBe(true) + expect(includes('abc', 'b', NaN)).toBe(true) + expect(includes('abc', 'b', 'x')).toBe(true) + expect(includes('abc', 'b', false)).toBe(true) + expect(includes('abc', 'b', undefined)).toBe(true) + expect(includes('abc', 'b', null)).toBe(true) + expect(includes('abc', 'b', 1)).toBe(true) + expect(includes('abc', 'b', 2)).toBe(false) + expect(includes('abc', 'b', 3)).toBe(false) + expect(includes('abc', 'b', 4)).toBe(false) + expect(includes('abc', 'b', Number(Infinity))).toBe(false) + }) + }) + + describe('regex searchString', () => { + it('throws TypeError for regex', () => { + expect(includes('foo[a-z]+(bar)?', '[a-z]+')).toBe(true) + expect(() => includes('foo[a-z]+(bar)?', /[a-z]+/)).toThrow(TypeError) + expect(includes('foo[a-z]+(bar)?', '(bar)?')).toBe(true) + expect(() => includes('foo[a-z]+(bar)?', /(bar)?/)).toThrow(TypeError) + }) + }) + + describe('nullish this object', () => { + it('throws TypeError for null/undefined', () => { + expect(() => includes(undefined)).toThrow(TypeError) + expect(() => includes(undefined, 'b')).toThrow(TypeError) + expect(() => includes(null)).toThrow(TypeError) + expect(() => includes(null, 'b')).toThrow(TypeError) + }) + }) + + describe('cast this object', () => { + it('converts this via ToString', () => { + expect(includes(42, '2')).toBe(true) + expect(includes(42, 'b', 4)).toBe(false) + expect( + includes( + { + toString: function () { + return 'abc' + }, + }, + 'b', + 0, + ), + ).toBe(true) + }) + }) +}) diff --git a/test/npm/string.prototype.matchall.test.mts b/test/npm/string.prototype.matchall.test.mts new file mode 100644 index 00000000..6bd33eb0 --- /dev/null +++ b/test/npm/string.prototype.matchall.test.mts @@ -0,0 +1,89 @@ +/** + * @fileoverview Tests for string.prototype.matchall NPM package override. + * Ported 1:1 from upstream v4.0.12 (c3d18708): + * https://github.com/es-shims/String.prototype.matchAll/blob/c3d18708/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: matchAll, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const collectResults = (iterator: any) => { + const results = [] + let result = iterator.next() + while (!result.done) { + results.push(result.value) + result = iterator.next() + } + return results +} + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('passing a string instead of a regex', () => { + const str = 'aabcaba' + const strResults = collectResults(matchAll(str, 'a')) + const regexResults = collectResults(matchAll(str, /a/g)) + expect(strResults.length).toBe(regexResults.length) + for (let i = 0; i < strResults.length; i++) { + expect(strResults[i][0]).toBe(regexResults[i][0]) + expect(strResults[i].index).toBe(regexResults[i].index) + } + }) + + it('returns an iterator', () => { + const str = 'aabc' + const iterator = matchAll(str, /[ac]/g) + expect(iterator).toBeTruthy() + expect(Object.prototype.hasOwnProperty.call(iterator, 'next')).toBe(false) + const results = collectResults(iterator) + expect(results.length).toBe(3) + expect(results[0][0]).toBe('a') + expect(results[0].index).toBe(0) + expect(results[1][0]).toBe('a') + expect(results[1].index).toBe(1) + expect(results[2][0]).toBe('c') + expect(results[2].index).toBe(3) + }) + + it('throws with a non-global regex', () => { + const str = 'AaBbCc' + expect(() => matchAll(str, /[bc]/i)).toThrow(TypeError) + }) + + it('works with a global regex', () => { + const str = 'AaBbCc' + const results = collectResults(matchAll(str, /[bc]/gi)) + expect(results.length).toBe(4) + expect(results[0][0]).toBe('B') + expect(results[1][0]).toBe('b') + expect(results[2][0]).toBe('C') + expect(results[3][0]).toBe('c') + }) + + it('respects flags', () => { + const str = 'A\na\nb\nC' + const results = collectResults(matchAll(str, /^[ac]/gim)) + expect(results.length).toBe(3) + expect(results[0][0]).toBe('A') + expect(results[1][0]).toBe('a') + expect(results[2][0]).toBe('C') + }) + + describe('zero-width matches', () => { + it('global', () => { + const str = 'abcde' + const results = collectResults(matchAll(str, /\B/g)) + expect(results.length).toBe(4) + expect(results[0].index).toBe(1) + expect(results[1].index).toBe(2) + expect(results[2].index).toBe(3) + expect(results[3].index).toBe(4) + }) + }) +}) diff --git a/test/npm/string.prototype.padend.test.mts b/test/npm/string.prototype.padend.test.mts new file mode 100644 index 00000000..0a820afb --- /dev/null +++ b/test/npm/string.prototype.padend.test.mts @@ -0,0 +1,53 @@ +/** + * @fileoverview Tests for string.prototype.padend NPM package override. + * Ported 1:1 from upstream v3.1.6 (674778e9): + * https://github.com/es-shims/String.prototype.padEnd/blob/674778e9/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: padEnd, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('pads end with single character', () => { + expect(padEnd('a', 3, 'b')).toBe('abb') + }) + + it('noops when string is already of maximum length', () => { + expect(padEnd('abc', 3, 'd')).toBe('abc') + }) + + it('noops when string is larger than maximum length', () => { + expect(padEnd('abc', -3, 'd')).toBe('abc') + }) + + it('pads when max length equals length plus filler', () => { + expect(padEnd('cd', 3, 'ab')).toBe('cda') + }) + + it('noops with absent maximum length', () => { + expect(padEnd('abc')).toBe('abc') + }) + + it('defaults fillStr to a space', () => { + expect(padEnd('a', 3)).toBe('a ') + }) + + it('stringifies non-string fillStr', () => { + expect(padEnd('ed', 6, null)).toBe('ednull') + }) + }) + + describe('truncated fill string', () => { + it('truncates at the provided max length', () => { + expect(padEnd('a', 2, 'bc')).toBe('ab') + }) + }) +}) diff --git a/test/npm/string.prototype.padstart.test.mts b/test/npm/string.prototype.padstart.test.mts new file mode 100644 index 00000000..52a776f2 --- /dev/null +++ b/test/npm/string.prototype.padstart.test.mts @@ -0,0 +1,53 @@ +/** + * @fileoverview Tests for string.prototype.padstart NPM package override. + * Ported 1:1 from upstream v3.1.7 (24c67699): + * https://github.com/es-shims/String.prototype.padStart/blob/24c67699/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: padStart, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('pads start with single character', () => { + expect(padStart('a', 3, 'b')).toBe('bba') + }) + + it('noops when string is already of maximum length', () => { + expect(padStart('abc', 3, 'd')).toBe('abc') + }) + + it('noops when string is larger than maximum length', () => { + expect(padStart('abc', -3, 'd')).toBe('abc') + }) + + it('pads when max length equals length plus filler', () => { + expect(padStart('cd', 3, 'ab')).toBe('acd') + }) + + it('noops with absent maximum length', () => { + expect(padStart('abc')).toBe('abc') + }) + + it('defaults fillStr to a space', () => { + expect(padStart('a', 3)).toBe(' a') + }) + + it('stringifies non-string fillStr', () => { + expect(padStart('ed', 6, null)).toBe('nulled') + }) + }) + + describe('truncated fill string', () => { + it('truncates at the provided max length', () => { + expect(padStart('a', 2, 'bc')).toBe('ba') + }) + }) +}) diff --git a/test/npm/string.prototype.repeat.test.mts b/test/npm/string.prototype.repeat.test.mts new file mode 100644 index 00000000..4607b4d8 --- /dev/null +++ b/test/npm/string.prototype.repeat.test.mts @@ -0,0 +1,72 @@ +/** + * @fileoverview Tests for string.prototype.repeat NPM package override. + * Ported 1:1 from upstream v1.0.0 (c9da0749): + * https://github.com/mathiasbynens/String.prototype.repeat/blob/c9da0749/tests/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: repeat, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('cast count argument', () => { + it('returns empty string for falsy counts', () => { + expect(repeat('abc')).toBe('') + expect(repeat('abc', undefined)).toBe('') + expect(repeat('abc', null)).toBe('') + expect(repeat('abc', false)).toBe('') + expect(repeat('abc', NaN)).toBe('') + expect(repeat('abc', 'abc')).toBe('') + }) + }) + + describe('invalid numeric count', () => { + it('throws RangeError', () => { + expect(() => repeat('abc', -Infinity)).toThrow(RangeError) + expect(() => repeat('abc', -1)).toThrow(RangeError) + expect(() => repeat('abc', +Infinity)).toThrow(RangeError) + }) + }) + + describe('valid numeric count', () => { + it('repeats correctly', () => { + expect(repeat('abc', -0)).toBe('') + expect(repeat('abc', +0)).toBe('') + expect(repeat('abc', 1)).toBe('abc') + expect(repeat('abc', 2)).toBe('abcabc') + expect(repeat('abc', 3)).toBe('abcabcabc') + expect(repeat('abc', 4)).toBe('abcabcabcabc') + }) + }) + + describe('nullish this object', () => { + it('throws TypeError', () => { + expect(() => repeat(undefined)).toThrow(TypeError) + expect(() => repeat(undefined, 4)).toThrow(TypeError) + expect(() => repeat(null)).toThrow(TypeError) + expect(() => repeat(null, 4)).toThrow(TypeError) + }) + }) + + describe('cast this object', () => { + it('converts this via ToString', () => { + expect(repeat(42, 4)).toBe('42424242') + expect( + repeat( + { + toString: function () { + return 'abc' + }, + }, + 2, + ), + ).toBe('abcabc') + }) + }) +}) diff --git a/test/npm/string.prototype.replaceall.test.mts b/test/npm/string.prototype.replaceall.test.mts new file mode 100644 index 00000000..33b03c4e --- /dev/null +++ b/test/npm/string.prototype.replaceall.test.mts @@ -0,0 +1,41 @@ +/** + * @fileoverview Tests for string.prototype.replaceall NPM package override. + * Ported 1:1 from upstream v1.0.11 (6c1ae0c6): + * https://github.com/es-shims/String.prototype.replaceAll/blob/6c1ae0c6/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: replaceAll, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws with a non-global regex', () => { + expect(() => replaceAll('abcabc', /a/, 'z')).toThrow(TypeError) + }) + + it('with a global regex matches replace with the same args', () => { + expect(replaceAll('abcabc', /a/g, 'z')).toBe('abcabc'.replace(/a/g, 'z')) + }) + + it('with a string replaces all occurrences', () => { + expect(replaceAll('abcabc', 'a', 'z')).toBe('zbczbc') + }) + + it('empty string replaces each code unit in single char string', () => { + expect(replaceAll('x', '', '_')).toBe('_x_') + }) + + it('empty string replaces each code unit in multi char string', () => { + expect(replaceAll('xxx', '', '_')).toBe('_x_x_x_') + }) + + it('empty regex replaces each code unit', () => { + expect(replaceAll('xxx', /(?:)/g, '_')).toBe('_x_x_x_') + }) +}) diff --git a/test/npm/string.prototype.split.test.mts b/test/npm/string.prototype.split.test.mts new file mode 100644 index 00000000..3f0ca6a2 --- /dev/null +++ b/test/npm/string.prototype.split.test.mts @@ -0,0 +1,152 @@ +/** + * @fileoverview Tests for string.prototype.split NPM package override. + * Ported 1:1 from upstream v1.0.9 (9aa9b69d): + * https://github.com/es-shims/String.prototype.split/blob/9aa9b69d/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: split, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('undefined separator returns array with receiver', () => { + expect(split('ab')).toEqual(['ab']) + expect(split('ab', undefined)).toEqual(['ab']) + }) + + it('zero limit returns empty array', () => { + expect(split('ab', undefined, 0)).toEqual([]) + }) + + describe('limit argument', () => { + it('respects limit', () => { + expect(split('a b', / /, 0)).toEqual([]) + expect(split('a b', / /, 1)).toEqual(['a']) + expect(split('a b', / /, 2)).toEqual(['a', 'b']) + expect(split('a b', / /, 3)).toEqual(['a', 'b']) + }) + }) + + describe('empty space receiver', () => { + it('handles empty strings', () => { + expect(split('')).toEqual(['']) + expect(split('', /./)).toEqual(['']) + expect(split('', /.?/)).toEqual([]) + expect(split('', /.??/)).toEqual([]) + }) + }) + + describe('extra tests', () => { + it('splits with various regex patterns', () => { + expect(split('ab', /a*/)).toEqual(['', 'b']) + expect(split('ab', /a*?/)).toEqual(['a', 'b']) + expect(split('ab', /(?:ab)/)).toEqual(['', '']) + expect(split('ab', /(?:ab)*/)).toEqual(['', '']) + expect(split('ab', /(?:ab)*?/)).toEqual(['a', 'b']) + }) + + it('splits with string separator', () => { + expect(split('test', '')).toEqual(['t', 'e', 's', 't']) + expect(split('test')).toEqual(['test']) + expect(split('111', 1)).toEqual(['', '', '', '']) + }) + + it('splits with empty regex and various limits', () => { + expect(split('test', /(?:)/, undefined)).toEqual(['t', 'e', 's', 't']) + expect(split('test', /(?:)/)).toEqual(['t', 'e', 's', 't']) + expect(split('test', /(?:)/, 4)).toEqual(['t', 'e', 's', 't']) + expect(split('test', /(?:)/, 3)).toEqual(['t', 'e', 's']) + expect(split('test', /(?:)/, 2)).toEqual(['t', 'e']) + expect(split('test', /(?:)/, 1)).toEqual(['t']) + expect(split('test', /(?:)/, 0)).toEqual([]) + }) + + it('splits with dash patterns', () => { + expect(split('a', /-/)).toEqual(['a']) + expect(split('a', /-?/)).toEqual(['a']) + expect(split('a', /-??/)).toEqual(['a']) + + expect(split('a-b', /-/)).toEqual(['a', 'b']) + expect(split('a-b', /-?/)).toEqual(['a', 'b']) + expect(split('a-b', /-??/)).toEqual(['a', '-', 'b']) + + expect(split('a--b', /-/)).toEqual(['a', '', 'b']) + expect(split('a--b', /-?/)).toEqual(['a', '', 'b']) + expect(split('a--b', /-??/)).toEqual(['a', '-', '-', 'b']) + }) + + it('splits with capturing groups', () => { + expect(split('test', 't')).toEqual(['', 'es', '']) + expect(split('test', /t/)).toEqual(['', 'es', '']) + expect(split('test', /(t)/)).toEqual(['', 't', 'es', 't', '']) + + expect(split('test', 'es')).toEqual(['t', 't']) + expect(split('test', /es/)).toEqual(['t', 't']) + expect(split('test', /(es)/)).toEqual(['t', 'es', 't']) + + expect(split('test', /(t)(e)(s)(t)/)).toEqual([ + '', + 't', + 'e', + 's', + 't', + '', + ]) + }) + + it('splits with complex HTML pattern', () => { + expect( + split('Aboldandcoded', /<(\/)?([^<>]+)>/), + ).toEqual([ + 'A', + undefined, + 'B', + 'bold', + '/', + 'B', + 'and', + undefined, + 'CODE', + 'coded', + '/', + 'CODE', + '', + ]) + }) + + it('splits with repeated capture groups', () => { + expect(split('tesst', /(s)*/)).toEqual(['t', undefined, 'e', 's', 't']) + expect(split('tesst', /(s)*?/)).toEqual([ + 't', + undefined, + 'e', + undefined, + 's', + undefined, + 's', + undefined, + 't', + ]) + expect(split('tesst', /(s*)/)).toEqual(['t', '', 'e', 'ss', 't']) + expect(split('tesst', /(s*?)/)).toEqual([ + 't', + '', + 'e', + '', + 's', + '', + 's', + '', + 't', + ]) + expect(split('tesst', /(?:s)*/)).toEqual(['t', 'e', 't']) + expect(split('tesst', /(?=s+)/)).toEqual(['te', 's', 'st']) + }) + }) +}) diff --git a/test/npm/string.prototype.startswith.test.mts b/test/npm/string.prototype.startswith.test.mts new file mode 100644 index 00000000..04a9aaae --- /dev/null +++ b/test/npm/string.prototype.startswith.test.mts @@ -0,0 +1,129 @@ +/** + * @fileoverview Tests for string.prototype.startswith NPM package override. + * Ported 1:1 from upstream v1.0.1 (10f437ce): + * https://github.com/mathiasbynens/String.prototype.startsWith/blob/10f437ce/tests/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: startsWith, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('"undefined" this string', () => { + it('handles undefined/null search', () => { + expect(startsWith('undefined')).toBe(true) + expect(startsWith('undefined', undefined)).toBe(true) + expect(startsWith('undefined', null)).toBe(false) + }) + }) + + describe('"null" this string', () => { + it('handles undefined/null search', () => { + expect(startsWith('null')).toBe(false) + expect(startsWith('null', undefined)).toBe(false) + expect(startsWith('null', null)).toBe(true) + }) + }) + + describe('without position argument', () => { + it('checks string start', () => { + expect(startsWith('abc')).toBe(false) + expect(startsWith('abc', '')).toBe(true) + expect(startsWith('abc', '\0')).toBe(false) + expect(startsWith('abc', 'a')).toBe(true) + expect(startsWith('abc', 'b')).toBe(false) + expect(startsWith('abc', 'ab')).toBe(true) + expect(startsWith('abc', 'bc')).toBe(false) + expect(startsWith('abc', 'abc')).toBe(true) + expect(startsWith('abc', 'bcd')).toBe(false) + expect(startsWith('abc', 'abcd')).toBe(false) + }) + }) + + describe('position 1', () => { + it('starts from position 1', () => { + expect(startsWith('abc', '', 1)).toBe(true) + expect(startsWith('abc', 'a', 1)).toBe(false) + expect(startsWith('abc', 'b', 1)).toBe(true) + expect(startsWith('abc', 'bc', 1)).toBe(true) + expect(startsWith('abc', 'abc', 1)).toBe(false) + }) + }) + + describe('position +Infinity', () => { + it('returns true only for empty search', () => { + expect(startsWith('abc', '', +Infinity)).toBe(true) + expect(startsWith('abc', 'a', +Infinity)).toBe(false) + expect(startsWith('abc', 'b', +Infinity)).toBe(false) + expect(startsWith('abc', 'abc', +Infinity)).toBe(false) + }) + }) + + describe('RegExp search string', () => { + it('throws TypeError for regex', () => { + expect(startsWith('[a-z]+(bar)?', '[a-z]+')).toBe(true) + expect(() => startsWith('[a-z]+(bar)?', /[a-z]+/)).toThrow(TypeError) + expect(startsWith('[a-z]+(bar)?', '(bar)?', 6)).toBe(true) + expect(() => startsWith('[a-z]+(bar)?', /(bar)?/)).toThrow(TypeError) + }) + }) + + describe('surrogate pairs', () => { + it('handles unicode strings', () => { + const string = + 'I\xF1t\xEBrn\xE2ti\xF4n\xE0liz\xE6ti\xF8n\u2603\uD83D\uDCA9' + expect(startsWith(string, '')).toBe(true) + expect(startsWith(string, '\xF1t\xEBr')).toBe(false) + expect(startsWith(string, '\xF1t\xEBr', 1)).toBe(true) + expect(startsWith(string, '\u2603')).toBe(false) + expect(startsWith(string, '\u2603', 20)).toBe(true) + expect(startsWith(string, '\uD83D\uDCA9')).toBe(false) + expect(startsWith(string, '\uD83D\uDCA9', 21)).toBe(true) + }) + }) + + describe('nullish this object', () => { + it('throws TypeError', () => { + expect(() => startsWith(undefined)).toThrow(TypeError) + expect(() => startsWith(undefined, 'b')).toThrow(TypeError) + expect(() => startsWith(null)).toThrow(TypeError) + expect(() => startsWith(null, 'b')).toThrow(TypeError) + }) + }) + + describe('cast this object', () => { + it('converts this via ToString', () => { + expect(startsWith(42, '2')).toBe(false) + expect(startsWith(42, '4')).toBe(true) + expect(startsWith(42, '2', 1)).toBe(true) + expect( + startsWith( + { + toString: function () { + return 'abc' + }, + }, + 'b', + 0, + ), + ).toBe(false) + expect( + startsWith( + { + toString: function () { + return 'abc' + }, + }, + 'b', + 1, + ), + ).toBe(true) + }) + }) +}) diff --git a/test/npm/string.prototype.trim.test.mts b/test/npm/string.prototype.trim.test.mts new file mode 100644 index 00000000..4d63adba --- /dev/null +++ b/test/npm/string.prototype.trim.test.mts @@ -0,0 +1,48 @@ +/** + * @fileoverview Tests for string.prototype.trim NPM package override. + * Ported 1:1 from upstream v1.2.10 (0ce6d13c): + * https://github.com/es-shims/String.prototype.trim/blob/0ce6d13c/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: trim, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('strips whitespace off left and right sides', () => { + expect(trim(' \t\na \t\n')).toBe('a') + }) + + it('noops when no whitespace', () => { + expect(trim('a')).toBe('a') + }) + + it('trims all expected whitespace chars', () => { + const allWhitespaceChars = + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + expect(trim(allWhitespaceChars + 'a' + allWhitespaceChars)).toBe('a') + }) + }) + + describe('zero-width spaces', () => { + it('does not trim zero-width space', () => { + const zeroWidth = '\u200b' + expect(trim(zeroWidth)).toBe(zeroWidth) + }) + }) + + describe('non-whitespace characters', () => { + it('does not trim non-whitespace', () => { + expect(trim('\u0085')).toBe('\u0085') + expect(trim('\u200b')).toBe('\u200b') + expect(trim('\ufffe')).toBe('\ufffe') + }) + }) +}) diff --git a/test/npm/string.prototype.trimend.test.mts b/test/npm/string.prototype.trimend.test.mts new file mode 100644 index 00000000..5e981638 --- /dev/null +++ b/test/npm/string.prototype.trimend.test.mts @@ -0,0 +1,42 @@ +/** + * @fileoverview Tests for string.prototype.trimend NPM package override. + * Ported 1:1 from upstream v1.0.9 (5f0347d6): + * https://github.com/es-shims/String.prototype.trimEnd/blob/5f0347d6/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: trimEnd, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('strips whitespace off the right side', () => { + expect(trimEnd(' \t\na \t\n')).toBe(' \t\na') + }) + + it('noops when no whitespace', () => { + expect(trimEnd('a')).toBe('a') + }) + + it('trims all expected whitespace chars from end', () => { + const allWhitespaceChars = + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + expect(trimEnd(allWhitespaceChars + 'a' + allWhitespaceChars)).toBe( + allWhitespaceChars + 'a', + ) + }) + }) + + describe('zero-width spaces', () => { + it('does not trim zero-width space', () => { + const zeroWidth = '\u200b' + expect(trimEnd(zeroWidth)).toBe(zeroWidth) + }) + }) +}) diff --git a/test/npm/string.prototype.trimleft.test.mts b/test/npm/string.prototype.trimleft.test.mts new file mode 100644 index 00000000..3a72cc97 --- /dev/null +++ b/test/npm/string.prototype.trimleft.test.mts @@ -0,0 +1,42 @@ +/** + * @fileoverview Tests for string.prototype.trimleft NPM package override. + * Ported 1:1 from upstream v2.1.3 (ff9bea31): + * https://github.com/es-shims/String.prototype.trimLeft/blob/ff9bea31/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: trimLeft, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('strips whitespace off the left side', () => { + expect(trimLeft(' \t\na \t\n')).toBe('a \t\n') + }) + + it('noops when no whitespace', () => { + expect(trimLeft('a')).toBe('a') + }) + + it('trims all expected whitespace chars from start', () => { + const allWhitespaceChars = + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + expect(trimLeft(allWhitespaceChars + 'a' + allWhitespaceChars)).toBe( + 'a' + allWhitespaceChars, + ) + }) + }) + + describe('zero-width spaces', () => { + it('does not trim zero-width space', () => { + const zeroWidth = '\u200b' + expect(trimLeft(zeroWidth)).toBe(zeroWidth) + }) + }) +}) diff --git a/test/npm/string.prototype.trimright.test.mts b/test/npm/string.prototype.trimright.test.mts new file mode 100644 index 00000000..c31b4695 --- /dev/null +++ b/test/npm/string.prototype.trimright.test.mts @@ -0,0 +1,42 @@ +/** + * @fileoverview Tests for string.prototype.trimright NPM package override. + * Ported 1:1 from upstream v2.1.3 (9228baa6): + * https://github.com/es-shims/String.prototype.trimRight/blob/9228baa6/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: trimRight, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('strips whitespace off the right side', () => { + expect(trimRight(' \t\na \t\n')).toBe(' \t\na') + }) + + it('noops when no whitespace', () => { + expect(trimRight('a')).toBe('a') + }) + + it('trims all expected whitespace chars from end', () => { + const allWhitespaceChars = + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + expect(trimRight(allWhitespaceChars + 'a' + allWhitespaceChars)).toBe( + allWhitespaceChars + 'a', + ) + }) + }) + + describe('zero-width spaces', () => { + it('does not trim zero-width space', () => { + const zeroWidth = '\u200b' + expect(trimRight(zeroWidth)).toBe(zeroWidth) + }) + }) +}) diff --git a/test/npm/string.prototype.trimstart.test.mts b/test/npm/string.prototype.trimstart.test.mts new file mode 100644 index 00000000..e2340328 --- /dev/null +++ b/test/npm/string.prototype.trimstart.test.mts @@ -0,0 +1,56 @@ +/** + * @fileoverview Tests for string.prototype.trimstart NPM package override. + * Ported 1:1 from upstream v1.0.8 (e0ebce2a): + * https://github.com/es-shims/String.prototype.trimStart/blob/e0ebce2a/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: trimStart, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('normal cases', () => { + it('strips whitespace off the left side', () => { + expect(trimStart(' \t\na \t\n')).toBe('a \t\n') + }) + + it('noops when no whitespace', () => { + expect(trimStart('a')).toBe('a') + }) + + it('trims all expected whitespace chars from start', () => { + const allWhitespaceChars = + '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF' + expect(trimStart(allWhitespaceChars + 'a' + allWhitespaceChars)).toBe( + 'a' + allWhitespaceChars, + ) + }) + }) + + describe('mongolian vowel separator', () => { + it('handles mongolian vowel separator based on unicode version', () => { + const mongolianVowelSeparator = '\u180E' + const mvsIsWS = /^\s$/.test(mongolianVowelSeparator) + expect( + trimStart(mongolianVowelSeparator + 'a' + mongolianVowelSeparator), + ).toBe( + (mvsIsWS ? '' : mongolianVowelSeparator) + + 'a' + + mongolianVowelSeparator, + ) + }) + }) + + describe('zero-width spaces', () => { + it('does not trim zero-width space', () => { + const zeroWidth = '\u200b' + expect(trimStart(zeroWidth)).toBe(zeroWidth) + }) + }) +}) diff --git a/test/npm/typed-array-buffer.test.mts b/test/npm/typed-array-buffer.test.mts new file mode 100644 index 00000000..82d47373 --- /dev/null +++ b/test/npm/typed-array-buffer.test.mts @@ -0,0 +1,64 @@ +/** + * @fileoverview Tests for typed-array-buffer NPM package override. + * Ported 1:1 from upstream v1.0.3 (a4141b67): + * https://github.com/inspect-js/typed-array-buffer/blob/a4141b67/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: typedArrayBuffer, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws for non-typed-array values', () => { + const nonTAs = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + [], + {}, + /a/g, + new Date(), + () => {}, + ] + for (const nonTA of nonTAs) { + expect(() => typedArrayBuffer(nonTA)).toThrow(TypeError) + } + }) + + it('returns the buffer of typed arrays', () => { + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA === 'function') { + const ta = new TA(0) + expect(typedArrayBuffer(ta)).toBe(ta.buffer) + } + } + }) +}) diff --git a/test/npm/typed-array-byte-length.test.mts b/test/npm/typed-array-byte-length.test.mts new file mode 100644 index 00000000..1dbead00 --- /dev/null +++ b/test/npm/typed-array-byte-length.test.mts @@ -0,0 +1,85 @@ +/** + * @fileoverview Tests for typed-array-byte-length NPM package override. + * Ported 1:1 from upstream v1.0.3 (1ff4b117): + * https://github.com/inspect-js/typed-array-byte-length/blob/1ff4b117/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: typedArrayByteLength, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not typed arrays', () => { + it('returns false for non-typed-array primitives', () => { + expect(typedArrayByteLength()).toBe(false) + expect(typedArrayByteLength(null)).toBe(false) + expect(typedArrayByteLength(false)).toBe(false) + expect(typedArrayByteLength(true)).toBe(false) + }) + + it('returns false for objects', () => { + expect(typedArrayByteLength({})).toBe(false) + expect(typedArrayByteLength(/a/g)).toBe(false) + expect(typedArrayByteLength(new RegExp('a', 'g'))).toBe(false) + expect(typedArrayByteLength(new Date())).toBe(false) + }) + + it('returns false for numbers', () => { + expect(typedArrayByteLength(42)).toBe(false) + expect(typedArrayByteLength(Object(42))).toBe(false) + expect(typedArrayByteLength(NaN)).toBe(false) + expect(typedArrayByteLength(Infinity)).toBe(false) + }) + + it('returns false for strings', () => { + expect(typedArrayByteLength('foo')).toBe(false) + expect(typedArrayByteLength(Object('foo'))).toBe(false) + }) + + it('returns false for functions', () => { + expect(typedArrayByteLength(function () {})).toBe(false) + }) + }) + + describe('Typed Arrays', () => { + it('returns correct byte length', () => { + const length = 64 + const byteOffset = 32 + + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA === 'function') { + const buffer = new ArrayBuffer(length) + const arr = new TA(buffer, byteOffset) + expect(typedArrayByteLength(arr)).toBe(byteOffset) + } + } + }) + + it('works with specific offset', () => { + const buffer = new ArrayBuffer(8) + const uint8 = new Uint8Array(buffer, 2) + expect(typedArrayByteLength(uint8)).toBe(6) + }) + }) +}) diff --git a/test/npm/typed-array-byte-offset.test.mts b/test/npm/typed-array-byte-offset.test.mts new file mode 100644 index 00000000..530f50bb --- /dev/null +++ b/test/npm/typed-array-byte-offset.test.mts @@ -0,0 +1,79 @@ +/** + * @fileoverview Tests for typed-array-byte-offset NPM package override. + * Ported 1:1 from upstream v1.0.4 (392d04c8): + * https://github.com/inspect-js/typed-array-byte-offset/blob/392d04c8/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: typedArrayByteOffset, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not typed arrays', () => { + it('returns false for non-typed-array primitives', () => { + expect(typedArrayByteOffset()).toBe(false) + expect(typedArrayByteOffset(null)).toBe(false) + expect(typedArrayByteOffset(false)).toBe(false) + expect(typedArrayByteOffset(true)).toBe(false) + }) + + it('returns false for objects', () => { + expect(typedArrayByteOffset({})).toBe(false) + expect(typedArrayByteOffset(/a/g)).toBe(false) + expect(typedArrayByteOffset(new RegExp('a', 'g'))).toBe(false) + expect(typedArrayByteOffset(new Date())).toBe(false) + }) + + it('returns false for numbers', () => { + expect(typedArrayByteOffset(42)).toBe(false) + expect(typedArrayByteOffset(Object(42))).toBe(false) + expect(typedArrayByteOffset(NaN)).toBe(false) + expect(typedArrayByteOffset(Infinity)).toBe(false) + }) + + it('returns false for strings', () => { + expect(typedArrayByteOffset('foo')).toBe(false) + expect(typedArrayByteOffset(Object('foo'))).toBe(false) + }) + + it('returns false for functions', () => { + expect(typedArrayByteOffset(function () {})).toBe(false) + }) + }) + + describe('Typed Arrays', () => { + it('returns correct byte offset', () => { + const length = 32 + const byteOffset = 16 + + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA === 'function') { + const buffer = new ArrayBuffer(length) + const arr = new TA(buffer, byteOffset) + expect(typedArrayByteOffset(arr)).toBe(byteOffset) + } + } + }) + }) +}) diff --git a/test/npm/typed-array-length.test.mts b/test/npm/typed-array-length.test.mts new file mode 100644 index 00000000..fb8f827a --- /dev/null +++ b/test/npm/typed-array-length.test.mts @@ -0,0 +1,76 @@ +/** + * @fileoverview Tests for typed-array-length NPM package override. + * Ported 1:1 from upstream v1.0.7 (25731a8c): + * https://github.com/inspect-js/typed-array-length/blob/25731a8c/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: typedArrayLength, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not typed arrays', () => { + it('returns false for non-typed-array primitives', () => { + expect(typedArrayLength()).toBe(false) + expect(typedArrayLength(null)).toBe(false) + expect(typedArrayLength(false)).toBe(false) + expect(typedArrayLength(true)).toBe(false) + }) + + it('returns false for objects', () => { + expect(typedArrayLength({})).toBe(false) + expect(typedArrayLength(/a/g)).toBe(false) + expect(typedArrayLength(new RegExp('a', 'g'))).toBe(false) + expect(typedArrayLength(new Date())).toBe(false) + }) + + it('returns false for numbers', () => { + expect(typedArrayLength(42)).toBe(false) + expect(typedArrayLength(Object(42))).toBe(false) + expect(typedArrayLength(NaN)).toBe(false) + expect(typedArrayLength(Infinity)).toBe(false) + }) + + it('returns false for strings', () => { + expect(typedArrayLength('foo')).toBe(false) + expect(typedArrayLength(Object('foo'))).toBe(false) + }) + + it('returns false for functions', () => { + expect(typedArrayLength(function () {})).toBe(false) + }) + }) + + describe('Typed Arrays', () => { + it('returns correct length', () => { + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA === 'function') { + const length = 10 + const arr = new TA(length) + expect(typedArrayLength(arr)).toBe(length) + } + } + }) + }) +}) diff --git a/test/npm/typedarray.prototype.slice.test.mts b/test/npm/typedarray.prototype.slice.test.mts new file mode 100644 index 00000000..73c8d85f --- /dev/null +++ b/test/npm/typedarray.prototype.slice.test.mts @@ -0,0 +1,85 @@ +/** + * @fileoverview Tests for typedarray.prototype.slice NPM package override. + * Ported 1:1 from upstream v1.0.5 (3d0a3276): + * https://github.com/es-shims/TypedArray.prototype.slice/blob/3d0a3276/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: slice, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('throws for non-typed-array values', () => { + const nonTAs = [ + undefined, + null, + true, + false, + 0, + 42, + NaN, + Infinity, + '', + 'foo', + [], + {}, + /a/g, + new Date(), + () => {}, + ] + for (const nonTA of nonTAs) { + expect(() => slice(nonTA)).toThrow(TypeError) + } + }) + + describe('Typed Arrays', () => { + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA !== 'function') continue + const isBigInt = name.slice(0, 3) === 'Big' + + it(`${name}: returns a new instance when sliced with no args`, () => { + const ta = new (TA as any)( + isBigInt ? [BigInt(1), BigInt(2), BigInt(3)] : [1, 2, 3], + ) + const copy = slice(ta) + expect(copy).not.toBe(ta) + expect(copy).toBeInstanceOf(TA) + expect(Array.from(copy)).toEqual(Array.from(ta)) + expect(copy.buffer).not.toBe(ta.buffer) + }) + + it(`${name}: returns subset when sliced with start index`, () => { + const ta = new (TA as any)( + isBigInt ? [BigInt(1), BigInt(2), BigInt(3)] : [1, 2, 3], + ) + const subset = slice(ta, 1) + expect(subset).not.toBe(ta) + const expected = new (TA as any)( + isBigInt ? [BigInt(2), BigInt(3)] : [2, 3], + ) + expect(Array.from(subset)).toEqual(Array.from(expected)) + }) + } + }) +}) diff --git a/test/npm/typedarray.test.mts b/test/npm/typedarray.test.mts new file mode 100644 index 00000000..825dde14 --- /dev/null +++ b/test/npm/typedarray.test.mts @@ -0,0 +1,24 @@ +/** + * @fileoverview Tests for typedarray NPM package override. + * Ported 1:1 from upstream v0.0.7 (f7387a01): + * https://github.com/es-shims/typedarray/blob/f7387a01/test/tarray.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: TA, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('tiny u8a test', () => { + const ua = new TA.Uint8Array(5) + expect(ua.length).toBe(5) + ua[1] = 256 + 55 + expect(ua[1]).toBe(55) + }) +}) diff --git a/test/npm/unbox-primitive.test.mts b/test/npm/unbox-primitive.test.mts new file mode 100644 index 00000000..bff57d3a --- /dev/null +++ b/test/npm/unbox-primitive.test.mts @@ -0,0 +1,48 @@ +/** + * @fileoverview Tests for unbox-primitive NPM package override. + * Ported 1:1 from upstream v1.1.0 (57a9506f): + * https://github.com/ljharb/unbox-primitive/blob/57a9506f/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: unboxPrimitive, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('primitives', () => { + it('throws for null and undefined', () => { + expect(() => unboxPrimitive(null)).toThrow(TypeError) + expect(() => unboxPrimitive(undefined)).toThrow(TypeError) + }) + + it('unboxes boxed primitives', () => { + expect(unboxPrimitive(Object(true))).toBe(true) + expect(unboxPrimitive(Object(false))).toBe(false) + expect(unboxPrimitive(Object(0))).toBe(0) + expect(unboxPrimitive(Object(42))).toBe(42) + expect(unboxPrimitive(Object(''))).toBe('') + expect(unboxPrimitive(Object('foo'))).toBe('foo') + + const sym = Symbol('test') + expect(unboxPrimitive(Object(sym))).toBe(sym) + + expect(unboxPrimitive(Object(BigInt(42)))).toBe(BigInt(42)) + }) + }) + + describe('objects', () => { + it('throws for non-boxed objects', () => { + expect(() => unboxPrimitive({})).toThrow(TypeError) + expect(() => unboxPrimitive([])).toThrow(TypeError) + expect(() => unboxPrimitive(function () {})).toThrow(TypeError) + expect(() => unboxPrimitive(/a/g)).toThrow(TypeError) + expect(() => unboxPrimitive(new Date())).toThrow(TypeError) + }) + }) +}) diff --git a/test/npm/util.promisify.test.mts b/test/npm/util.promisify.test.mts new file mode 100644 index 00000000..892129de --- /dev/null +++ b/test/npm/util.promisify.test.mts @@ -0,0 +1,58 @@ +/** + * @fileoverview Tests for util.promisify NPM package override. + * Ported 1:1 from upstream v1.1.3 (4e6f368e): + * https://github.com/ljharb/util.promisify/blob/4e6f368e/test/tests.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: promisify, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('is a function', () => { + expect(typeof promisify).toBe('function') + }) + + it('throws on non-functions', () => { + expect(() => promisify(null)).toThrow(TypeError) + }) + + it('pYes is properly promisified', async () => { + const yes = function (...args: any[]) { + const cb = args[args.length - 1] + cb(null, args.slice(0, -1)) + } + const pYes = promisify(yes) + expect(typeof pYes).toBe('function') + + const result = await pYes(1, 2, 3) + expect(result).toEqual([1, 2, 3]) + }) + + it('pNo is properly promisified', async () => { + const no = function (...args: any[]) { + const cb = args[args.length - 1] + cb(args.slice(0, -1)) + } + const pNo = promisify(no) + expect(typeof pNo).toBe('function') + + try { + await pNo(1, 2, 3) + expect.fail('should have rejected') + } catch (err) { + expect(err).toEqual([1, 2, 3]) + } + }) + + it('custom symbol', () => { + expect(Symbol.keyFor(promisify.custom)).toBe('nodejs.util.promisify.custom') + expect(Symbol.for('nodejs.util.promisify.custom')).toBe(promisify.custom) + }) +}) diff --git a/test/npm/which-boxed-primitive.test.mts b/test/npm/which-boxed-primitive.test.mts new file mode 100644 index 00000000..bea847dc --- /dev/null +++ b/test/npm/which-boxed-primitive.test.mts @@ -0,0 +1,61 @@ +/** + * @fileoverview Tests for which-boxed-primitive NPM package override. + * Ported 1:1 from upstream v1.1.1 (7f06bcbc): + * https://github.com/inspect-js/which-boxed-primitive/blob/7f06bcbc/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: whichBoxedPrimitive, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('unboxed primitives', () => { + it('returns null for primitives', () => { + const primitives = [ + undefined, + null, + true, + false, + 0, + -0, + 42, + NaN, + Infinity, + '', + 'foo', + ] + for (const primitive of primitives) { + expect(whichBoxedPrimitive(primitive)).toBe(null) + } + }) + }) + + describe('boxed primitives', () => { + it('returns the constructor name for boxed primitives', () => { + expect(whichBoxedPrimitive(Object(true))).toBe('Boolean') + expect(whichBoxedPrimitive(Object(false))).toBe('Boolean') + expect(whichBoxedPrimitive(Object(0))).toBe('Number') + expect(whichBoxedPrimitive(Object(42))).toBe('Number') + expect(whichBoxedPrimitive(Object(NaN))).toBe('Number') + expect(whichBoxedPrimitive(Object(''))).toBe('String') + expect(whichBoxedPrimitive(Object('foo'))).toBe('String') + expect(whichBoxedPrimitive(Object(Symbol('test')))).toBe('Symbol') + expect(whichBoxedPrimitive(Object(BigInt(42)))).toBe('BigInt') + }) + }) + + describe('non-primitive objects', () => { + it('returns undefined for non-boxed objects', () => { + const objects = [/a/g, new Date(), function () {}, [], {}] + for (const obj of objects) { + expect(whichBoxedPrimitive(obj)).toBe(undefined) + } + }) + }) +}) diff --git a/test/npm/which-collection.test.mts b/test/npm/which-collection.test.mts new file mode 100644 index 00000000..1a69dce0 --- /dev/null +++ b/test/npm/which-collection.test.mts @@ -0,0 +1,56 @@ +/** + * @fileoverview Tests for which-collection NPM package override. + * Ported 1:1 from upstream v1.0.2 (fda9470d): + * https://github.com/inspect-js/which-collection/blob/fda9470d/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: whichCollection, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + it('returns false for non-collections', () => { + const nonCollections = [ + null, + undefined, + true, + false, + 42, + 0, + -0, + NaN, + Infinity, + '', + 'foo', + /a/g, + [], + {}, + function () {}, + ] + for (const nonCollection of nonCollections) { + expect(whichCollection(nonCollection)).toBe(false) + } + }) + + it('returns "Map" for Maps', () => { + expect(whichCollection(new Map())).toBe('Map') + }) + + it('returns "Set" for Sets', () => { + expect(whichCollection(new Set())).toBe('Set') + }) + + it('returns "WeakMap" for WeakMaps', () => { + expect(whichCollection(new WeakMap())).toBe('WeakMap') + }) + + it('returns "WeakSet" for WeakSets', () => { + expect(whichCollection(new WeakSet())).toBe('WeakSet') + }) +}) diff --git a/test/npm/which-typed-array.test.mts b/test/npm/which-typed-array.test.mts new file mode 100644 index 00000000..0fbc8be5 --- /dev/null +++ b/test/npm/which-typed-array.test.mts @@ -0,0 +1,87 @@ +/** + * @fileoverview Tests for which-typed-array NPM package override. + * Ported 1:1 from upstream v1.1.20 (2710ad21): + * https://github.com/inspect-js/which-typed-array/blob/2710ad21/test/index.js + */ +import { describe, expect, it } from 'vitest' + +import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' + +const { + eco, + module: whichTypedArray, + skip, + sockRegPkgName, +} = await setupNpmPackageTest(import.meta.url) + +const typedArrayNames = [ + 'Int8Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Int16Array', + 'Uint16Array', + 'Int32Array', + 'Uint32Array', + 'Float32Array', + 'Float64Array', + 'BigInt64Array', + 'BigUint64Array', +] as const + +describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { + describe('not typed arrays', () => { + it('returns false for non-typed-array primitives', () => { + expect(whichTypedArray()).toBe(false) + expect(whichTypedArray(null)).toBe(false) + expect(whichTypedArray(false)).toBe(false) + expect(whichTypedArray(true)).toBe(false) + }) + + it('returns false for objects', () => { + expect(whichTypedArray({})).toBe(false) + expect(whichTypedArray(/a/g)).toBe(false) + expect(whichTypedArray(new RegExp('a', 'g'))).toBe(false) + expect(whichTypedArray(new Date())).toBe(false) + }) + + it('returns false for numbers', () => { + expect(whichTypedArray(42)).toBe(false) + expect(whichTypedArray(Object(42))).toBe(false) + expect(whichTypedArray(NaN)).toBe(false) + expect(whichTypedArray(Infinity)).toBe(false) + }) + + it('returns false for strings', () => { + expect(whichTypedArray('foo')).toBe(false) + expect(whichTypedArray(Object('foo'))).toBe(false) + }) + + it('returns false for functions', () => { + expect(whichTypedArray(function () {})).toBe(false) + }) + }) + + describe('@@toStringTag fakes', () => { + it('returns false for faked typed arrays', () => { + for (const name of typedArrayNames) { + if (typeof globalThis[name] === 'function') { + const fakeTypedArray: any = [] + fakeTypedArray[Symbol.toStringTag] = name + expect(whichTypedArray(fakeTypedArray)).toBe(false) + } + } + }) + }) + + describe('Typed Arrays', () => { + it('returns the correct type name', () => { + for (const name of typedArrayNames) { + const TA = globalThis[name] + if (typeof TA === 'function') { + const arr = new TA(10) + expect(whichTypedArray(arr)).toBe(name) + } + } + }) + }) +}) diff --git a/test/utils/npm-package-helper.mts b/test/utils/npm-package-helper.mts index 756ba04e..8710cedc 100644 --- a/test/utils/npm-package-helper.mts +++ b/test/utils/npm-package-helper.mts @@ -1,16 +1,13 @@ /** - * @fileoverview Helper utilities for NPM package testing. - * Provides standardized setup for package installation and testing. + * @fileoverview Helper for NPM package testing. + * Loads override modules directly from packages/npm/ without installing. */ import path from 'node:path' import { NPM, NPM_PACKAGES_PATH } from '../../scripts/constants/paths.mjs' -import { installPackageForTesting } from '../../scripts/utils/package.mjs' import { isPackageTestingSkipped } from '../../scripts/utils/tests.mjs' -const npmPackagesPath = NPM_PACKAGES_PATH - interface SetupNpmPackageTestResult { eco: string pkgPath: string @@ -20,21 +17,11 @@ interface SetupNpmPackageTestResult { } /** - * Sets up an NPM package test environment with standard boilerplate. - * - * @param filename - The test filename (typically __filename or import.meta.url). - * @returns Promise - Object containing test context and installed package. + * Sets up an NPM package test by loading the module from packages/npm/. * * @example - * import { setupNpmPackageTest } from '../utils/npm-package-helper.mts' - * - * const { module: assert, pkgPath, skip, eco, sockRegPkgName } = await setupNpmPackageTest(__filename) - * - * describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { - * it('should work', () => { - * expect(assert).toBeDefined() - * }) - * }) + * const { module: myMod, skip, eco, sockRegPkgName } = await setupNpmPackageTest(import.meta.url) + * describe(`${eco} > ${sockRegPkgName}`, { skip }, () => { ... }) */ export async function setupNpmPackageTest( filename: string, @@ -42,54 +29,16 @@ export async function setupNpmPackageTest( const sockRegPkgName = path.basename(filename, '.test.mts') const eco = NPM const skip = isPackageTestingSkipped(eco, sockRegPkgName) - - let pkgPath = '' + const pkgPath = path.join(NPM_PACKAGES_PATH, sockRegPkgName) let module: any if (!skip) { - const result = await installPackageForTesting( - npmPackagesPath, - sockRegPkgName, - ) - if (!result.installed) { - throw new Error(`Failed to install package: ${result.reason}`) - } - if (!result.packagePath) { - throw new Error('Package path is undefined after installation') + try { + module = require(pkgPath) + } catch { + return { eco, module: undefined, pkgPath, skip: true, sockRegPkgName } } - pkgPath = result.packagePath - module = require(pkgPath) - } - - return { - eco, - module, - pkgPath, - skip, - sockRegPkgName, } -} -/** - * Creates a beforeAll hook that sets up an NPM package test. - * Useful for simpler test files that just need the setup in beforeAll. - * - * @param filename - The test filename (typically __filename). - * @param callback - Callback to receive the setup result. - * - * @example - * createNpmPackageBeforeAll(__filename, ({ module, pkgPath }) => { - * assert = module - * testPkgPath = pkgPath - * }) - */ -export function createNpmPackageBeforeAll( - filename: string, - callback: (result: Omit) => void, -): () => Promise { - return async () => { - const { eco, module, pkgPath, sockRegPkgName } = - await setupNpmPackageTest(filename) - callback({ eco, module, pkgPath, sockRegPkgName }) - } + return { eco, module, pkgPath, skip, sockRegPkgName } }