diff --git a/docs/index.html b/docs/index.html index 836f76b..c37894e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -489,48 +489,6 @@

GitHub Feature Support Table

Polyfilled Features

- - - - ClipboardItem - - -
*
-
66+ †
-
79+ †
-
87+ †
-
13.1+
-
53+ †
-
9.0+ †
- - - - - Element.checkVisibility - - -
*
-
105+
-
105+
-
106+
-
17.4+
-
91+
-
20.0+
- - - - - navigator.clipboard - - -
*
-
86+
-
79+
-
63+ †
-
13.1+
-
63+ †
-
12.0+ †
- @@ -545,34 +503,6 @@

GitHub Feature Support Table

34+
5.0+
- - -
- HTMLElement.popover - - -
*
-
114+
-
114+
-
125+
-
17+
-
100+
-
23.0+
- - - - - HTMLElement.popover = 'hint' - - -
*
-
114+
-
114+
-
125+
-
17+
-
+
-
12.0+
- diff --git a/package-lock.json b/package-lock.json index 33305f1..ca10751 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,6 @@ "version": "1.2.2", "license": "MIT", "dependencies": { - "@oddbird/popover-polyfill": "^0.5.2", "invokers-polyfill": "^0.5.2" }, "devDependencies": { @@ -693,12 +692,6 @@ "node": ">= 8" } }, - "node_modules/@oddbird/popover-polyfill": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@oddbird/popover-polyfill/-/popover-polyfill-0.5.2.tgz", - "integrity": "sha512-iFrvar5SOMtKFOSjYvs4z9UlLqDdJbMx0mgISLcPedv+g0ac5sgeETLGtipHCVIae6HJPclNEH5aCyD1RZaEHw==", - "license": "BSD-3-Clause" - }, "node_modules/@pkgr/utils": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", diff --git a/package.json b/package.json index b4f105b..c51e496 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,6 @@ "typescript": "^5.2.2" }, "dependencies": { - "@oddbird/popover-polyfill": "^0.5.2", "invokers-polyfill": "^0.5.2" }, "overrides": { diff --git a/src/clipboarditem.ts b/src/clipboarditem.ts deleted file mode 100644 index d478806..0000000 --- a/src/clipboarditem.ts +++ /dev/null @@ -1,47 +0,0 @@ -type ClipboardItems = Record> -const records = new WeakMap() -const presentationStyles = new WeakMap() -export class ClipboardItem { - constructor(items: ClipboardItems, options: ClipboardItemOptions | undefined = {}) { - if (Object.keys(items).length === 0) throw new TypeError('Empty dictionary argument') - records.set(this, items) - presentationStyles.set(this, options.presentationStyle || 'unspecified') - } - - get presentationStyle(): PresentationStyle { - return presentationStyles.get(this) || 'unspecified' - } - - get types() { - return Object.freeze(Object.keys(records.get(this) || {})) - } - - async getType(type: string): Promise { - const record = records.get(this) - if (record && type in record) { - const item = await record[type]! - if (typeof item === 'string') return new Blob([item], {type}) - return item - } - throw new DOMException("Failed to execute 'getType' on 'ClipboardItem': The type was not found", 'NotFoundError') - } -} - -export function isSupported(): boolean { - try { - new globalThis.ClipboardItem({'text/plain': Promise.resolve('')}) - return true - } catch { - return false - } -} - -export function isPolyfilled(): boolean { - return globalThis.ClipboardItem === ClipboardItem -} - -export function apply(): void { - if (!isSupported()) { - globalThis.ClipboardItem = ClipboardItem - } -} diff --git a/src/element-checkvisibility.ts b/src/element-checkvisibility.ts deleted file mode 100644 index 6b50ddb..0000000 --- a/src/element-checkvisibility.ts +++ /dev/null @@ -1,50 +0,0 @@ -declare global { - interface Element { - checkVisibility(options?: Partial): boolean - } -} - -interface CheckVisibilityOptions { - checkOpacity: boolean - checkVisibilityCSS: boolean -} - -export function checkVisibility( - this: Element, - {checkOpacity = false, checkVisibilityCSS = false}: Partial = {}, -) { - if (!this.isConnected) return false - const styles = getComputedStyle(this) - if (styles.getPropertyValue('display') === 'contents') return false - if (checkVisibilityCSS && styles.getPropertyValue('visibility') !== 'visible') return false - // eslint-disable-next-line @typescript-eslint/no-this-alias - let node: Element | null = this - while (node) { - const nodeStyles = node === this ? styles : getComputedStyle(node) - if (nodeStyles.getPropertyValue('display') === 'none') return false - if (checkOpacity && nodeStyles.getPropertyValue('opacity') === '0') return false - if (node !== this && nodeStyles.getPropertyValue('content-visibility') === 'hidden') { - return false - } - if (!node.parentElement && node.getRootNode() instanceof ShadowRoot) { - node = (node.getRootNode() as ShadowRoot).host - } else { - node = node.parentElement - } - } - return true -} - -export function isSupported(): boolean { - return 'checkVisibility' in Element.prototype && typeof Element.prototype.checkVisibility === 'function' -} - -export function isPolyfilled(): boolean { - return Element.prototype.checkVisibility === checkVisibility -} - -export function apply(): void { - if (!isSupported()) { - Element.prototype.checkVisibility = checkVisibility - } -} diff --git a/src/index.ts b/src/index.ts index 0e03ff2..68b675b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,5 @@ -import * as clipboardItem from './clipboarditem.js' -import * as elementCheckVisibility from './element-checkvisibility.js' -import * as navigatorClipboard from './navigator-clipboard.js' -import * as withResolvers from './promise-withResolvers.js' import * as requestIdleCallback from './requestidlecallback.js' -import * as popover from '@oddbird/popover-polyfill/fn' import * as commandAndCommandFor from 'invokers-polyfill/fn' -import * as objectGroupBy from './object-groupby.js' -import * as mapGroupBy from './map-groupby.js' -import * as promiseTry from './promise-try.js' -import * as iteratorHelpers from './iterator-helpers.js' -import * as setMethods from './set-methods.js' let supportsModalPseudo = false try { @@ -51,18 +41,8 @@ export const baseSupport = true export const polyfills = { - clipboardItem, - elementCheckVisibility, - navigatorClipboard, requestIdleCallback, - withResolvers, - popover, commandAndCommandFor, - objectGroupBy, - mapGroupBy, - promiseTry, - iteratorHelpers, - setMethods, } export function isSupported() { diff --git a/src/iterator-helpers.ts b/src/iterator-helpers.ts deleted file mode 100644 index af98ee5..0000000 --- a/src/iterator-helpers.ts +++ /dev/null @@ -1,410 +0,0 @@ -// Get the Iterator prototype -/*#__PURE__*/ -function getIteratorPrototype(): Iterator | null { - try { - // Derive IteratorPrototype from the Array iterator prototype chain. - // This avoids dynamic evaluation and works under strict CSP. - if (typeof Symbol !== 'function' || typeof Symbol.iterator !== 'symbol') { - return null - } - const arrayIter = [][Symbol.iterator]() - const arrayIterProto = Object.getPrototypeOf(arrayIter) - if (!arrayIterProto) return null - return Object.getPrototypeOf(arrayIterProto) as Iterator | null - } catch { - return null - } -} - -// Fallback prototype used when native IteratorPrototype cannot be discovered. -// Helper-produced iterators inherit from this so that method chaining still -// works even in environments where getIteratorPrototype() returns null. -const helperPrototype: Record = {} - -/*#__PURE__*/ -function map(this: Iterator, mapper: (value: T, index: number) => U): Iterator { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let index = 0 - const iteratorPrototype = getIteratorPrototype() - const result = Object.create(iteratorPrototype || helperPrototype) as Iterator - result.next = function () { - const res = iterator.next() - if (res.done) { - return {done: true, value: undefined} as IteratorReturnResult - } - return {done: false, value: mapper(res.value, index++)} - } - result.return = function (value?: U) { - if (iterator.return) { - iterator.return() - } - return {done: true, value} as IteratorReturnResult - } - return result -} - -/*#__PURE__*/ -function filter(this: Iterator, predicate: (value: T, index: number) => boolean): Iterator { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let index = 0 - const iteratorPrototype = getIteratorPrototype() - const result = Object.create(iteratorPrototype || helperPrototype) as Iterator - result.next = function () { - // eslint-disable-next-line no-constant-condition - while (true) { - const res = iterator.next() - if (res.done) { - return {done: true, value: undefined} as IteratorReturnResult - } - if (predicate(res.value, index++)) { - return res - } - } - } - result.return = function (value?: T) { - if (iterator.return) { - iterator.return() - } - return {done: true, value} as IteratorReturnResult - } - return result -} - -/*#__PURE__*/ -function take(this: Iterator, limit: number): Iterator { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let remaining = limit - const iteratorPrototype = getIteratorPrototype() - const result = Object.create(iteratorPrototype || helperPrototype) as Iterator - result.next = function () { - if (remaining <= 0) { - if (iterator.return) { - iterator.return() - } - return {done: true, value: undefined} as IteratorReturnResult - } - remaining-- - return iterator.next() - } - result.return = function (value?: T) { - if (iterator.return) { - iterator.return() - } - return {done: true, value} as IteratorReturnResult - } - return result -} - -/*#__PURE__*/ -function drop(this: Iterator, limit: number): Iterator { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let remaining = limit - const iteratorPrototype = getIteratorPrototype() - const result = Object.create(iteratorPrototype || helperPrototype) as Iterator - result.next = function () { - while (remaining > 0) { - const res = iterator.next() - if (res.done) { - return {done: true, value: undefined} as IteratorReturnResult - } - remaining-- - } - return iterator.next() - } - result.return = function (value?: T) { - if (iterator.return) { - iterator.return() - } - return {done: true, value} as IteratorReturnResult - } - return result -} - -/*#__PURE__*/ -function flatMap(this: Iterator, mapper: (value: T, index: number) => Iterable | Iterator): Iterator { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let index = 0 - let innerIterator: Iterator | null = null - const iteratorPrototype = getIteratorPrototype() - const result = Object.create(iteratorPrototype || helperPrototype) as Iterator - result.next = function () { - // eslint-disable-next-line no-constant-condition - while (true) { - if (innerIterator) { - const res = innerIterator.next() - if (!res.done) { - return res - } - innerIterator = null - } - - const res = iterator.next() - if (res.done) { - return {done: true, value: undefined} as IteratorReturnResult - } - - const mapped = mapper(res.value, index++) - innerIterator = Symbol.iterator in mapped ? mapped[Symbol.iterator]() : (mapped as Iterator) - } - } - result.return = function (value?: U) { - if (innerIterator?.return) { - innerIterator.return() - } - if (iterator.return) { - iterator.return() - } - return {done: true, value} as IteratorReturnResult - } - return result -} - -/*#__PURE__*/ -function reduce(this: Iterator, reducer: (accumulator: T, value: T, index: number) => T): T -/*#__PURE__*/ -function reduce(this: Iterator, reducer: (accumulator: U, value: T, index: number) => U, initialValue: U): U -/*#__PURE__*/ -function reduce( - this: Iterator, - reducer: (accumulator: U, value: T, index: number) => U, - ...rest: [initialValue: U] | [] -): U { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const iterator = this - let index = 0 - let accumulator: U - - // Use rest.length to reliably detect whether an initialValue was provided, - // including the case where undefined is intentionally passed as initialValue. - if (rest.length === 0) { - const result = iterator.next() - if (result.done) { - throw new TypeError('Reduce of empty iterator with no initial value') - } - accumulator = result.value as unknown as U - index = 1 - } else { - accumulator = rest[0] - } - - // eslint-disable-next-line no-constant-condition - while (true) { - const result = iterator.next() - if (result.done) { - return accumulator - } - accumulator = reducer(accumulator, result.value, index++) - } -} - -/*#__PURE__*/ -function toArray(this: Iterator): T[] { - const result: T[] = [] - // eslint-disable-next-line no-constant-condition - while (true) { - const next = this.next() - if (next.done) { - return result - } - result.push(next.value) - } -} - -/*#__PURE__*/ -function forEach(this: Iterator, callback: (value: T, index: number) => void): void { - let index = 0 - // eslint-disable-next-line no-constant-condition - while (true) { - const result = this.next() - if (result.done) { - return - } - callback(result.value, index++) - } -} - -/*#__PURE__*/ -function some(this: Iterator, predicate: (value: T, index: number) => boolean): boolean { - let index = 0 - // eslint-disable-next-line no-constant-condition - while (true) { - const result = this.next() - if (result.done) { - return false - } - if (predicate(result.value, index++)) { - if (this.return) { - this.return() - } - return true - } - } -} - -/*#__PURE__*/ -function every(this: Iterator, predicate: (value: T, index: number) => boolean): boolean { - let index = 0 - // eslint-disable-next-line no-constant-condition - while (true) { - const result = this.next() - if (result.done) { - return true - } - if (!predicate(result.value, index++)) { - if (this.return) { - this.return() - } - return false - } - } -} - -/*#__PURE__*/ -function find(this: Iterator, predicate: (value: T, index: number) => boolean): T | undefined { - let index = 0 - // eslint-disable-next-line no-constant-condition - while (true) { - const result = this.next() - if (result.done) { - return undefined - } - if (predicate(result.value, index++)) { - if (this.return) { - this.return() - } - return result.value - } - } -} - -/*#__PURE__*/ -function iteratorFrom(obj: Iterator | Iterable): Iterator { - if (obj != null) { - // Check for iterator (an object with a .next method) - if (typeof obj === 'object' && 'next' in obj && typeof (obj as Iterator).next === 'function') { - return obj as Iterator - } - // Box primitives (e.g. strings) so we can check Symbol.iterator on them - const iterable = Object(obj) as {[Symbol.iterator]?: () => Iterator} - if (Symbol.iterator in iterable && typeof iterable[Symbol.iterator] === 'function') { - return iterable[Symbol.iterator]!() - } - } - throw new TypeError('Object is not an iterator or iterable') -} - -/*#__PURE__*/ -export function isSupported(): boolean { - const IteratorPrototype = getIteratorPrototype() - const IteratorConstructor = (globalThis as typeof globalThis & {Iterator?: {from?: unknown}}).Iterator - return ( - IteratorPrototype !== null && - 'map' in IteratorPrototype && - 'filter' in IteratorPrototype && - 'take' in IteratorPrototype && - 'drop' in IteratorPrototype && - 'flatMap' in IteratorPrototype && - 'reduce' in IteratorPrototype && - 'toArray' in IteratorPrototype && - 'forEach' in IteratorPrototype && - 'some' in IteratorPrototype && - 'every' in IteratorPrototype && - 'find' in IteratorPrototype && - IteratorConstructor !== undefined && - (typeof IteratorConstructor === 'object' || typeof IteratorConstructor === 'function') && - IteratorConstructor !== null && - 'from' in IteratorConstructor - ) -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - const IteratorPrototype = getIteratorPrototype() - const IteratorConstructor = (globalThis as typeof globalThis & {Iterator?: {from?: unknown}}).Iterator - return ( - IteratorPrototype !== null && - 'map' in IteratorPrototype && - IteratorPrototype.map === map && - 'filter' in IteratorPrototype && - IteratorPrototype.filter === filter && - 'take' in IteratorPrototype && - IteratorPrototype.take === take && - 'drop' in IteratorPrototype && - IteratorPrototype.drop === drop && - 'flatMap' in IteratorPrototype && - IteratorPrototype.flatMap === flatMap && - 'reduce' in IteratorPrototype && - IteratorPrototype.reduce === reduce && - 'toArray' in IteratorPrototype && - IteratorPrototype.toArray === toArray && - 'forEach' in IteratorPrototype && - IteratorPrototype.forEach === forEach && - 'some' in IteratorPrototype && - IteratorPrototype.some === some && - 'every' in IteratorPrototype && - IteratorPrototype.every === every && - 'find' in IteratorPrototype && - IteratorPrototype.find === find && - IteratorConstructor !== undefined && - 'from' in IteratorConstructor && - IteratorConstructor.from === iteratorFrom - ) -} - -export function apply(): void { - if (isSupported()) return - - const IteratorPrototype = getIteratorPrototype() - // Install methods on the native IteratorPrototype when available, falling - // back to helperPrototype so that iterators produced by helpers can still - // chain even when the native prototype cannot be discovered. - const targetPrototype = (IteratorPrototype || helperPrototype) as Record - - if (!('map' in targetPrototype)) { - Object.assign(targetPrototype, {map}) - } - if (!('filter' in targetPrototype)) { - Object.assign(targetPrototype, {filter}) - } - if (!('take' in targetPrototype)) { - Object.assign(targetPrototype, {take}) - } - if (!('drop' in targetPrototype)) { - Object.assign(targetPrototype, {drop}) - } - if (!('flatMap' in targetPrototype)) { - Object.assign(targetPrototype, {flatMap}) - } - if (!('reduce' in targetPrototype)) { - Object.assign(targetPrototype, {reduce}) - } - if (!('toArray' in targetPrototype)) { - Object.assign(targetPrototype, {toArray}) - } - if (!('forEach' in targetPrototype)) { - Object.assign(targetPrototype, {forEach}) - } - if (!('some' in targetPrototype)) { - Object.assign(targetPrototype, {some}) - } - if (!('every' in targetPrototype)) { - Object.assign(targetPrototype, {every}) - } - if (!('find' in targetPrototype)) { - Object.assign(targetPrototype, {find}) - } - - let IteratorConstructor = (globalThis as typeof globalThis & {Iterator?: {from?: unknown}}).Iterator - if (!IteratorConstructor) { - ;(globalThis as typeof globalThis & {Iterator?: unknown}).Iterator = {} - IteratorConstructor = (globalThis as typeof globalThis & {Iterator?: {from?: unknown}}).Iterator - } - if (IteratorConstructor && !('from' in IteratorConstructor)) { - Object.assign(IteratorConstructor, {from: iteratorFrom}) - } -} diff --git a/src/map-groupby.ts b/src/map-groupby.ts deleted file mode 100644 index 390739d..0000000 --- a/src/map-groupby.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*#__PURE__*/ -export function groupBy( - this: MapConstructor, - items: Iterable, - keySelector: (item: T, index: number) => K, -): Map { - const result = new Map() - let index = 0 - for (const item of items) { - const key = keySelector(item, index++) - if (result.has(key)) { - result.get(key)!.push(item) - } else { - result.set(key, [item]) - } - } - return result -} - -/*#__PURE__*/ -export function isSupported(): boolean { - return 'groupBy' in Map && typeof Map.groupBy === 'function' -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - return 'groupBy' in Map && Map.groupBy === groupBy -} - -export function apply(): void { - if (!isSupported()) { - Object.assign(Map, {groupBy}) - } -} diff --git a/src/navigator-clipboard.ts b/src/navigator-clipboard.ts deleted file mode 100644 index 937e48f..0000000 --- a/src/navigator-clipboard.ts +++ /dev/null @@ -1,33 +0,0 @@ -export async function clipboardWrite(data: ClipboardItems) { - if (data.length === 0) return - const item = data[0] - const blob = await item.getType(item.types.includes('text/plain') ? 'text/plain' : item.types[0]) - return navigator.clipboard.writeText(typeof blob == 'string' ? blob : await blob.text()) -} - -export async function clipboardRead() { - const str = navigator.clipboard.readText() - return [new ClipboardItem({'text/plain': str})] -} - -export function isSupported(): boolean { - return ( - 'clipboard' in navigator && - typeof navigator.clipboard.read === 'function' && - typeof navigator.clipboard.write === 'function' - ) -} - -export function isPolyfilled(): boolean { - return ( - 'clipboard' in navigator && - (navigator.clipboard.write === clipboardWrite || navigator.clipboard.read === clipboardRead) - ) -} - -export function apply(): void { - if ('clipboard' in navigator && !isSupported()) { - navigator.clipboard.write = clipboardWrite - navigator.clipboard.read = clipboardRead - } -} diff --git a/src/object-groupby.ts b/src/object-groupby.ts deleted file mode 100644 index ac7d80f..0000000 --- a/src/object-groupby.ts +++ /dev/null @@ -1,34 +0,0 @@ -/*#__PURE__*/ -export function groupBy( - this: ObjectConstructor, - items: Iterable, - keySelector: (item: T, index: number) => K, -): Partial> { - const result = Object.create(null) as Partial> - let index = 0 - for (const item of items) { - const key = keySelector(item, index++) - if (Object.prototype.hasOwnProperty.call(result, key)) { - result[key]!.push(item) - } else { - result[key] = [item] - } - } - return result -} - -/*#__PURE__*/ -export function isSupported(): boolean { - return 'groupBy' in Object && typeof Object.groupBy === 'function' -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - return 'groupBy' in Object && Object.groupBy === groupBy -} - -export function apply(): void { - if (!isSupported()) { - Object.assign(Object, {groupBy}) - } -} diff --git a/src/promise-try.ts b/src/promise-try.ts deleted file mode 100644 index fc11e9a..0000000 --- a/src/promise-try.ts +++ /dev/null @@ -1,20 +0,0 @@ -/*#__PURE__*/ -export function promiseTry(this: PromiseConstructor, func: () => T | PromiseLike): Promise { - return new Promise(resolve => resolve(func())) -} - -/*#__PURE__*/ -export function isSupported(): boolean { - return 'try' in Promise && typeof Promise.try === 'function' -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - return 'try' in Promise && Promise.try === promiseTry -} - -export function apply(): void { - if (!isSupported()) { - Object.assign(Promise, {try: promiseTry}) - } -} diff --git a/src/promise-withResolvers.ts b/src/promise-withResolvers.ts deleted file mode 100644 index 04be228..0000000 --- a/src/promise-withResolvers.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*#__PURE__*/ -export function withResolvers(this: PromiseConstructor) { - const out = {} as { - promise: Promise - resolve: (value: T | PromiseLike) => void - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reject: (reason?: any) => void - } - out.promise = new Promise((resolve, reject) => { - out.resolve = resolve - out.reject = reject - }) - return out -} - -/*#__PURE__*/ -export function isSupported(): boolean { - return 'withResolvers' in Promise && typeof Promise.withResolvers === 'function' -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - return 'withResolvers' in Promise && Promise.withResolvers === withResolvers -} - -export function apply(): void { - if (!isSupported()) { - Object.assign(Promise, {withResolvers}) - } -} diff --git a/src/set-methods.ts b/src/set-methods.ts deleted file mode 100644 index b9b1d67..0000000 --- a/src/set-methods.ts +++ /dev/null @@ -1,131 +0,0 @@ -// Set-like interface per TC39 spec: must have has, keys, and size -interface SetLike { - has(value: T): boolean - keys(): IterableIterator - readonly size: number -} - -/*#__PURE__*/ -export function union(this: Set, other: SetLike): Set { - const result = new Set(this) - for (const value of other.keys()) { - result.add(value) - } - return result -} - -/*#__PURE__*/ -export function intersection(this: Set, other: SetLike): Set { - const result = new Set() - for (const value of this) { - if (other.has(value)) { - result.add(value) - } - } - return result -} - -/*#__PURE__*/ -export function difference(this: Set, other: SetLike): Set { - const result = new Set() - for (const value of this) { - if (!other.has(value)) { - result.add(value) - } - } - return result -} - -/*#__PURE__*/ -export function symmetricDifference(this: Set, other: SetLike): Set { - const result = new Set(this) - for (const value of other.keys()) { - if (result.has(value)) { - result.delete(value) - } else { - result.add(value) - } - } - return result -} - -/*#__PURE__*/ -export function isSubsetOf(this: Set, other: SetLike): boolean { - if (this.size > other.size) return false - for (const value of this) { - if (!other.has(value)) { - return false - } - } - return true -} - -/*#__PURE__*/ -export function isSupersetOf(this: Set, other: SetLike): boolean { - for (const value of other.keys()) { - if (!this.has(value)) { - return false - } - } - return true -} - -/*#__PURE__*/ -export function isDisjointFrom(this: Set, other: SetLike): boolean { - if (this.size <= other.size) { - for (const value of this) { - if (other.has(value)) { - return false - } - } - } else { - for (const value of other.keys()) { - if (this.has(value)) { - return false - } - } - } - return true -} - -/*#__PURE__*/ -export function isSupported(): boolean { - return ( - 'union' in Set.prototype && - 'intersection' in Set.prototype && - 'difference' in Set.prototype && - 'symmetricDifference' in Set.prototype && - 'isSubsetOf' in Set.prototype && - 'isSupersetOf' in Set.prototype && - 'isDisjointFrom' in Set.prototype - ) -} - -/*#__PURE__*/ -export function isPolyfilled(): boolean { - const proto = Set.prototype as unknown as Record - return ( - 'union' in Set.prototype && - proto['union'] === union && - proto['intersection'] === intersection && - proto['difference'] === difference && - proto['symmetricDifference'] === symmetricDifference && - proto['isSubsetOf'] === isSubsetOf && - proto['isSupersetOf'] === isSupersetOf && - proto['isDisjointFrom'] === isDisjointFrom - ) -} - -export function apply(): void { - if (!isSupported()) { - Object.assign(Set.prototype, { - union, - intersection, - difference, - symmetricDifference, - isSubsetOf, - isSupersetOf, - isDisjointFrom, - }) - } -} diff --git a/test/clipboarditem.js b/test/clipboarditem.js deleted file mode 100644 index 28943d1..0000000 --- a/test/clipboarditem.js +++ /dev/null @@ -1,18 +0,0 @@ -import {expect} from 'chai' -import {ClipboardItem, apply, isSupported, isPolyfilled} from '../src/clipboarditem.ts' - -describe('ClipboardItem', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('takes a Promise type, that can resolve', async () => { - const c = new ClipboardItem({'text/plain': Promise.resolve('hi')}) - expect(c.types).to.eql(['text/plain']) - expect(await c.getType('text/plain')).to.be.instanceof(Blob) - }) -}) diff --git a/test/element-checkvisibility.js b/test/element-checkvisibility.js deleted file mode 100644 index e4f3a1c..0000000 --- a/test/element-checkvisibility.js +++ /dev/null @@ -1,96 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported, checkVisibility} from '../src/element-checkvisibility.ts' - -describe('checkVisibility', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('checks visibility of elements', async () => { - // These tests originate from - // https://github.com/web-platform-tests/wpt/blob/master/css/cssom-view/checkVisibility.html - const el = document.createElement('div') - // eslint-disable-next-line github/no-inner-html - el.innerHTML = ` - -
-
hello
-
-
-
hello
-
- - -
-
hello
-
-
hello
-
-
slotted
-
-
-
-
-
-
-
slotted
-
-
spacer
-
-
hello
-
-
-
-
-
-
-
-
- ` - document.body.append(el) - for (const host of document.querySelectorAll('.shadow-host-with-slot')) { - const shadowRoot = host.attachShadow({mode: 'open'}) - const slot = document.createElement('slot') - slot.name = 'slot' - shadowRoot.appendChild(slot) - } - expect(checkVisibility.call(document.getElementById('visibilityhidden'), {checkVisibilityCSS: true})).to.equal( - false, - ) - expect(checkVisibility.call(document.getElementById('visibilityhidden'), {checkVisibilityCSS: false})).to.equal( - true, - ) - expect(checkVisibility.call(document.getElementById('cvhidden'))).to.equal(false) - expect(checkVisibility.call(document.getElementById('slottedincvhidden'))).to.equal(false) - expect(checkVisibility.call(document.getElementById('cvauto'))).to.equal(true) - expect(checkVisibility.call(document.getElementById('cvautooffscreen'))).to.equal(true) - expect(checkVisibility.call(document.getElementById('displaynone'))).to.equal(false) - expect(checkVisibility.call(document.getElementById('slottedindisplaynone'))).to.equal(false) - expect(checkVisibility.call(document.getElementById('displaycontents'))).to.equal(false) - expect(checkVisibility.call(document.getElementById('displaycontentschild'))).to.equal(true) - expect(checkVisibility.call(document.getElementById('opacityzero'), {checkOpacity: true})).to.equal(false) - expect(checkVisibility.call(document.getElementById('opacityzero'), {checkOpacity: false})).to.equal(true) - expect(checkVisibility.call(document.getElementById('slottedinopacityzero'), {checkOpacity: true})).to.equal(false) - expect(checkVisibility.call(document.getElementById('slottedinopacityzero'), {checkOpacity: false})).to.equal(true) - const cvautocontainer = document.getElementById('cvautocontainer') - const cvautochild = document.getElementById('cvautochild') - cvautocontainer.style.contentVisibility = 'auto' - cvautochild.style.visibility = 'hidden' - expect(checkVisibility.call(cvautochild, {checkVisibilityCSS: true})).to.equal(false) - cvautochild.style.visibility = 'visible' - expect(checkVisibility.call(cvautochild, {checkVisibilityCSS: true})).to.equal(true) - expect(checkVisibility.call(document.getElementById('nestedcvautochild'))).to.equal(true) - const cvhiddenchildwithupdate = document.getElementById('cvhiddenchildwithupdate') - cvhiddenchildwithupdate.getBoundingClientRect() - expect(checkVisibility.call(cvhiddenchildwithupdate)).to.equal(false) - const cvhiddenwithupdate = document.getElementById('cvhiddenwithupdate') - cvhiddenwithupdate.getBoundingClientRect() - expect(checkVisibility.call(cvhiddenwithupdate)).to.equal(true) - }) -}) diff --git a/test/iterator-helpers.js b/test/iterator-helpers.js deleted file mode 100644 index 1c717e8..0000000 --- a/test/iterator-helpers.js +++ /dev/null @@ -1,303 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported} from '../src/iterator-helpers.ts' - -// eslint-disable-next-line i18n-text/no-en -describe('Iterator helpers', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('isSupported returns true when Iterator is a function (native support)', () => { - // Native Iterator in Chromium is a function, not an object. - // Simulate this by temporarily replacing globalThis.Iterator with a function. - const original = globalThis.Iterator - try { - apply() // ensure polyfill is applied first so prototype methods exist - const proto = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) - // Create a function-based Iterator with a `from` method, like native Chromium - globalThis.Iterator = function Iterator() {} - globalThis.Iterator.from = function from() {} - // Assign all required methods to the iterator prototype - for (const method of [ - 'map', - 'filter', - 'take', - 'drop', - 'flatMap', - 'reduce', - 'toArray', - 'forEach', - 'some', - 'every', - 'find', - ]) { - if (!(method in proto)) { - proto[method] = function () {} - } - } - expect(isSupported()).to.equal(true) - } finally { - globalThis.Iterator = original - } - }) - - // Helper to create an iterator from an array - function* arrayIterator(arr) { - for (const item of arr) { - yield item - } - } - - describe('map', () => { - beforeEach(() => apply()) - - it('maps values', () => { - const iter = arrayIterator([1, 2, 3]) - const mapped = iter.map(x => x * 2) - expect([...mapped]).to.eql([2, 4, 6]) - }) - - it('passes index to mapper', () => { - const iter = arrayIterator(['a', 'b', 'c']) - const mapped = iter.map((x, i) => `${i}:${x}`) - expect([...mapped]).to.eql(['0:a', '1:b', '2:c']) - }) - }) - - describe('filter', () => { - beforeEach(() => apply()) - - it('filters values', () => { - const iter = arrayIterator([1, 2, 3, 4, 5]) - const filtered = iter.filter(x => x % 2 === 0) - expect([...filtered]).to.eql([2, 4]) - }) - - it('passes index to predicate', () => { - const iter = arrayIterator(['a', 'b', 'c', 'd']) - const filtered = iter.filter((_, i) => i % 2 === 0) - expect([...filtered]).to.eql(['a', 'c']) - }) - }) - - describe('take', () => { - beforeEach(() => apply()) - - it('takes first n values', () => { - const iter = arrayIterator([1, 2, 3, 4, 5]) - const taken = iter.take(3) - expect([...taken]).to.eql([1, 2, 3]) - }) - - it('handles taking more than available', () => { - const iter = arrayIterator([1, 2]) - const taken = iter.take(5) - expect([...taken]).to.eql([1, 2]) - }) - }) - - describe('drop', () => { - beforeEach(() => apply()) - - it('drops first n values', () => { - const iter = arrayIterator([1, 2, 3, 4, 5]) - const dropped = iter.drop(2) - expect([...dropped]).to.eql([3, 4, 5]) - }) - - it('handles dropping more than available', () => { - const iter = arrayIterator([1, 2]) - const dropped = iter.drop(5) - expect([...dropped]).to.eql([]) - }) - }) - - describe('flatMap', () => { - beforeEach(() => apply()) - - it('flattens mapped values', () => { - const iter = arrayIterator([1, 2, 3]) - const flatMapped = iter.flatMap(x => [x, x * 2]) - expect([...flatMapped]).to.eql([1, 2, 2, 4, 3, 6]) - }) - - it('handles empty results', () => { - const iter = arrayIterator([1, 2, 3]) - const flatMapped = iter.flatMap(x => (x % 2 === 0 ? [x] : [])) - expect([...flatMapped]).to.eql([2]) - }) - }) - - describe('reduce', () => { - beforeEach(() => apply()) - - it('reduces with initial value', () => { - const iter = arrayIterator([1, 2, 3, 4]) - const sum = iter.reduce((acc, x) => acc + x, 0) - expect(sum).to.equal(10) - }) - - it('reduces without initial value uses first element as accumulator', () => { - const iter = arrayIterator([1, 2, 3, 4]) - const calls = [] - iter.reduce((acc, x, i) => { - calls.push({acc, x, index: i}) - return acc + x - }) - // First element (1) is used as accumulator; reducer is called starting at index 1 - expect(calls).to.eql([ - {acc: 1, x: 2, index: 1}, - {acc: 3, x: 3, index: 2}, - {acc: 6, x: 4, index: 3}, - ]) - }) - - it('reduces without initial value returns correct sum', () => { - const iter = arrayIterator([1, 2, 3, 4]) - const sum = iter.reduce((acc, x) => acc + x) - expect(sum).to.equal(10) - }) - - it('throws on empty iterator without initial value', () => { - const iter = arrayIterator([]) - expect(() => iter.reduce((acc, x) => acc + x)).to.throw(TypeError) - }) - - it('treats explicit undefined as a provided initial value', () => { - const iter = arrayIterator([1, 2, 3]) - const calls = [] - iter.reduce((acc, x, i) => { - calls.push({acc, x, index: i}) - return x - }, undefined) - // undefined is the initial accumulator; reducer is called starting at index 0 - expect(calls[0]).to.eql({acc: undefined, x: 1, index: 0}) - expect(calls).to.have.lengthOf(3) - }) - }) - - describe('toArray', () => { - beforeEach(() => apply()) - - it('converts iterator to array', () => { - const iter = arrayIterator([1, 2, 3]) - expect(iter.toArray()).to.eql([1, 2, 3]) - }) - - it('handles empty iterator', () => { - const iter = arrayIterator([]) - expect(iter.toArray()).to.eql([]) - }) - }) - - describe('forEach', () => { - beforeEach(() => apply()) - - it('calls callback for each value', () => { - const iter = arrayIterator([1, 2, 3]) - const results = [] - // eslint-disable-next-line github/array-foreach - iter.forEach((x, i) => results.push({value: x, index: i})) - expect(results).to.eql([ - {value: 1, index: 0}, - {value: 2, index: 1}, - {value: 3, index: 2}, - ]) - }) - }) - - describe('some', () => { - beforeEach(() => apply()) - - it('returns true if any value matches', () => { - const iter = arrayIterator([1, 2, 3, 4]) - expect(iter.some(x => x > 3)).to.be.true - }) - - it('returns false if no value matches', () => { - const iter = arrayIterator([1, 2, 3]) - expect(iter.some(x => x > 5)).to.be.false - }) - }) - - describe('every', () => { - beforeEach(() => apply()) - - it('returns true if all values match', () => { - const iter = arrayIterator([2, 4, 6]) - expect(iter.every(x => x % 2 === 0)).to.be.true - }) - - it('returns false if any value does not match', () => { - const iter = arrayIterator([2, 3, 4]) - expect(iter.every(x => x % 2 === 0)).to.be.false - }) - }) - - describe('find', () => { - beforeEach(() => apply()) - - it('returns first matching value', () => { - const iter = arrayIterator([1, 2, 3, 4]) - expect(iter.find(x => x > 2)).to.equal(3) - }) - - it('returns undefined if no match', () => { - const iter = arrayIterator([1, 2, 3]) - expect(iter.find(x => x > 5)).to.be.undefined - }) - }) - - describe('Iterator.from', () => { - beforeEach(() => apply()) - - it('converts iterable to iterator', () => { - const IteratorConstructor = globalThis.Iterator - if (!IteratorConstructor?.from) { - // Skip if Iterator.from is not available after polyfill - return - } - const iter = IteratorConstructor.from([1, 2, 3]) - expect([...iter]).to.eql([1, 2, 3]) - }) - - it('returns iterator as-is', () => { - const IteratorConstructor = globalThis.Iterator - if (!IteratorConstructor?.from) { - // Skip if Iterator.from is not available after polyfill - return - } - const original = arrayIterator([1, 2, 3]) - const iter = IteratorConstructor.from(original) - expect(iter).to.equal(original) - }) - - it('handles primitive iterable strings', () => { - const IteratorConstructor = globalThis.Iterator - if (!IteratorConstructor?.from) { - // Skip if Iterator.from is not available after polyfill - return - } - const iter = IteratorConstructor.from('abc') - expect([...iter]).to.eql(['a', 'b', 'c']) - }) - }) - - describe('chaining', () => { - beforeEach(() => apply()) - - it('chains multiple operations', () => { - const iter = arrayIterator([1, 2, 3, 4, 5, 6]) - const result = iter - .filter(x => x % 2 === 0) - .map(x => x * 2) - .take(2) - .toArray() - expect(result).to.eql([4, 8]) - }) - }) -}) diff --git a/test/map-groupby.js b/test/map-groupby.js deleted file mode 100644 index e632ccb..0000000 --- a/test/map-groupby.js +++ /dev/null @@ -1,58 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported, groupBy} from '../src/map-groupby.ts' - -describe('Map.groupBy', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('groups items by key', () => { - const items = [ - {type: 'fruit', name: 'apple'}, - {type: 'vegetable', name: 'carrot'}, - {type: 'fruit', name: 'banana'}, - {type: 'vegetable', name: 'broccoli'}, - ] - const result = groupBy(items, item => item.type) - expect(result).to.be.instanceOf(Map) - expect(result.has('fruit')).to.be.true - expect(result.get('fruit')).to.have.lengthOf(2) - expect(result.get('fruit')?.[0]).to.eql({type: 'fruit', name: 'apple'}) - expect(result.get('fruit')?.[1]).to.eql({type: 'fruit', name: 'banana'}) - expect(result.has('vegetable')).to.be.true - expect(result.get('vegetable')).to.have.lengthOf(2) - expect(result.get('vegetable')?.[0]).to.eql({type: 'vegetable', name: 'carrot'}) - expect(result.get('vegetable')?.[1]).to.eql({type: 'vegetable', name: 'broccoli'}) - }) - - it('passes index to callback', () => { - const items = ['a', 'b', 'c', 'd'] - const result = groupBy(items, (_item, index) => (index % 2 === 0 ? 'even' : 'odd')) - expect(result.get('even')).to.eql(['a', 'c']) - expect(result.get('odd')).to.eql(['b', 'd']) - }) - - it('handles empty arrays', () => { - const result = groupBy([], () => 'key') - expect(result.size).to.equal(0) - }) - - it('works with object keys', () => { - const keyA = {id: 1} - const keyB = {id: 2} - const items = [ - {key: keyA, value: 'a'}, - {key: keyB, value: 'b'}, - {key: keyA, value: 'c'}, - ] - const result = groupBy(items, item => item.key) - expect(result.has(keyA)).to.be.true - expect(result.get(keyA)).to.have.lengthOf(2) - expect(result.has(keyB)).to.be.true - expect(result.get(keyB)).to.have.lengthOf(1) - }) -}) diff --git a/test/navigator-clipboard.js b/test/navigator-clipboard.js deleted file mode 100644 index c13d6c0..0000000 --- a/test/navigator-clipboard.js +++ /dev/null @@ -1,55 +0,0 @@ -import {expect} from 'chai' -import {clipboardRead, clipboardWrite, apply, isPolyfilled, isSupported} from '../src/navigator-clipboard.ts' - -describe('navigator clipboard', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - describe('read', () => { - it('read returns array of 1 clipboard entry with plaintext of readText value', async () => { - navigator.clipboard.readText = () => Promise.resolve('foo') - const arr = await clipboardRead() - expect(arr).to.have.lengthOf(1) - expect(arr[0]).to.be.an.instanceof(globalThis.ClipboardItem) - expect(arr[0].types).to.eql(['text/plain']) - expect(await (await arr[0].getType('text/plain')).text()).to.eql('foo') - }) - }) - - describe('write', () => { - it('unpacks text/plain content to writeText', async () => { - const calls = [] - navigator.clipboard.writeText = (...args) => calls.push(args) - await clipboardWrite([ - new globalThis.ClipboardItem({ - 'foo/bar': 'horrible', - 'text/plain': Promise.resolve('foo'), - }), - ]) - expect(calls).to.have.lengthOf(1) - expect(calls[0]).to.eql(['foo']) - }) - - it('accepts multiple clipboard items, picking the first', async () => { - const calls = [] - navigator.clipboard.writeText = (...args) => calls.push(args) - await clipboardWrite([ - new globalThis.ClipboardItem({ - 'foo/bar': 'horrible', - 'text/plain': Promise.resolve('multiple-pass'), - }), - new globalThis.ClipboardItem({ - 'foo/bar': 'multiple-fail', - 'text/plain': Promise.resolve('multiple-fail'), - }), - ]) - expect(calls).to.have.lengthOf(1) - expect(calls[0]).to.eql(['multiple-pass']) - }) - }) -}) diff --git a/test/object-groupby.js b/test/object-groupby.js deleted file mode 100644 index 6d469e3..0000000 --- a/test/object-groupby.js +++ /dev/null @@ -1,61 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported, groupBy} from '../src/object-groupby.ts' - -describe('Object.groupBy', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('groups items by key', () => { - const items = [ - {type: 'fruit', name: 'apple'}, - {type: 'vegetable', name: 'carrot'}, - {type: 'fruit', name: 'banana'}, - {type: 'vegetable', name: 'broccoli'}, - ] - const result = groupBy(items, item => item.type) - expect(result).to.have.property('fruit') - expect(result.fruit).to.have.lengthOf(2) - expect(result.fruit?.[0]).to.eql({type: 'fruit', name: 'apple'}) - expect(result.fruit?.[1]).to.eql({type: 'fruit', name: 'banana'}) - expect(result).to.have.property('vegetable') - expect(result.vegetable).to.have.lengthOf(2) - expect(result.vegetable?.[0]).to.eql({type: 'vegetable', name: 'carrot'}) - expect(result.vegetable?.[1]).to.eql({type: 'vegetable', name: 'broccoli'}) - }) - - it('passes index to callback', () => { - const items = ['a', 'b', 'c', 'd'] - const result = groupBy(items, (_item, index) => (index % 2 === 0 ? 'even' : 'odd')) - expect(result.even).to.eql(['a', 'c']) - expect(result.odd).to.eql(['b', 'd']) - }) - - it('handles empty arrays', () => { - const result = groupBy([], () => 'key') - expect(Object.keys(result)).to.have.lengthOf(0) - }) - - it('works with numeric keys', () => { - const items = [1, 2, 3, 4, 5, 6] - const result = groupBy(items, item => item % 3) - expect(result[0]).to.eql([3, 6]) - expect(result[1]).to.eql([1, 4]) - expect(result[2]).to.eql([2, 5]) - }) - - it('handles __proto__ key safely without prototype pollution', () => { - const items = ['a', 'b', 'c'] - const result = groupBy(items, (_item, index) => (index === 0 ? '__proto__' : 'other')) - // The __proto__ key should exist as an own property, not affect Object.prototype - expect(Object.prototype.hasOwnProperty.call(result, '__proto__')).to.be.true - expect(result['__proto__']).to.eql(['a']) - expect(result['other']).to.eql(['b', 'c']) - // Object.prototype should not be polluted - expect({}['__proto__']).to.not.eql(['a']) - }) -}) diff --git a/test/promise-try.js b/test/promise-try.js deleted file mode 100644 index 244e0b7..0000000 --- a/test/promise-try.js +++ /dev/null @@ -1,49 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported, promiseTry} from '../src/promise-try.ts' - -describe('Promise.try', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('resolves with the return value of a successful function', async () => { - const result = await promiseTry(() => 42) - expect(result).to.equal(42) - }) - - it('resolves with the resolved value of a returned promise', async () => { - const result = await promiseTry(() => Promise.resolve(42)) - expect(result).to.equal(42) - }) - - it('rejects with the error thrown by the function', async () => { - const error = new Error('rejected') - try { - await promiseTry(() => { - throw error - }) - expect.fail('should fail') - } catch (e) { - expect(e).to.equal(error) - } - }) - - it('rejects with the rejected value of a returned promise', async () => { - const error = new Error('rejected') - try { - await promiseTry(() => Promise.reject(error)) - expect.fail('should fail') - } catch (e) { - expect(e).to.equal(error) - } - }) - - it('handles synchronous values', async () => { - const result = await promiseTry(() => 'sync') - expect(result).to.equal('sync') - }) -}) diff --git a/test/promise-withResolvers.js b/test/promise-withResolvers.js deleted file mode 100644 index 3038634..0000000 --- a/test/promise-withResolvers.js +++ /dev/null @@ -1,41 +0,0 @@ -import {expect} from 'chai' -import {apply, isPolyfilled, isSupported, withResolvers} from '../src/promise-withResolvers.ts' - -describe('withResolvers', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - it('resolves to first resolving value', async () => { - const arg = withResolvers() - expect(Object.keys(arg).sort()).to.eql(['promise', 'reject', 'resolve']) - expect(arg).to.have.property('promise').to.be.a('promise') - expect(arg).to.have.property('resolve').to.be.a('function') - expect(arg).to.have.property('reject').to.be.a('function') - - arg.resolve(1) - expect(await arg.promise).to.be.eql(1) - }) - - it('rejects to first rejecting reason', async () => { - const arg = withResolvers() - expect(Object.keys(arg).sort()).to.eql(['promise', 'reject', 'resolve']) - expect(arg).to.have.property('promise').to.be.a('promise') - expect(arg).to.have.property('resolve').to.be.a('function') - expect(arg).to.have.property('reject').to.be.a('function') - - const err = new Error('rejected') - - try { - arg.reject(err) - await arg.promise - expect.fail('should fail') - } catch (e) { - expect(e).to.be.eql(err) - } - }) -}) diff --git a/test/set-methods.js b/test/set-methods.js deleted file mode 100644 index bfecbb8..0000000 --- a/test/set-methods.js +++ /dev/null @@ -1,148 +0,0 @@ -import {expect} from 'chai' -import { - apply, - isPolyfilled, - isSupported, - union, - intersection, - difference, - symmetricDifference, - isSubsetOf, - isSupersetOf, - isDisjointFrom, -} from '../src/set-methods.ts' - -// eslint-disable-next-line i18n-text/no-en -describe('Set methods', () => { - it('has standard isSupported, isPolyfilled, apply API', () => { - expect(isSupported).to.be.a('function') - expect(isPolyfilled).to.be.a('function') - expect(apply).to.be.a('function') - expect(isSupported()).to.be.a('boolean') - expect(isPolyfilled()).to.equal(false) - }) - - describe('union', () => { - it('returns a new Set with elements from both sets', () => { - const a = new Set([1, 2, 3]) - const b = new Set([3, 4, 5]) - const result = union.call(a, b) - expect(result).to.be.instanceof(Set) - expect([...result].sort()).to.eql([1, 2, 3, 4, 5]) - }) - - it('handles empty sets', () => { - const a = new Set([1, 2]) - const result = union.call(a, new Set()) - expect([...result].sort()).to.eql([1, 2]) - }) - }) - - describe('intersection', () => { - it('returns a new Set with elements in both sets', () => { - const a = new Set([1, 2, 3]) - const b = new Set([2, 3, 4]) - const result = intersection.call(a, b) - expect(result).to.be.instanceof(Set) - expect([...result].sort()).to.eql([2, 3]) - }) - - it('returns empty set when no common elements', () => { - const a = new Set([1, 2]) - const b = new Set([3, 4]) - const result = intersection.call(a, b) - expect(result.size).to.equal(0) - }) - }) - - describe('difference', () => { - it('returns a new Set with elements in this but not other', () => { - const a = new Set([1, 2, 3]) - const b = new Set([2, 3, 4]) - const result = difference.call(a, b) - expect(result).to.be.instanceof(Set) - expect([...result]).to.eql([1]) - }) - - it('returns a copy of this when no overlap', () => { - const a = new Set([1, 2]) - const b = new Set([3, 4]) - const result = difference.call(a, b) - expect([...result].sort()).to.eql([1, 2]) - }) - }) - - describe('symmetricDifference', () => { - it('returns elements in one set but not both', () => { - const a = new Set([1, 2, 3]) - const b = new Set([2, 3, 4]) - const result = symmetricDifference.call(a, b) - expect(result).to.be.instanceof(Set) - expect([...result].sort()).to.eql([1, 4]) - }) - - it('returns empty set for identical sets', () => { - const a = new Set([1, 2]) - const result = symmetricDifference.call(a, new Set([1, 2])) - expect(result.size).to.equal(0) - }) - }) - - describe('isSubsetOf', () => { - it('returns true when all elements are in the other set', () => { - const a = new Set([1, 2]) - const b = new Set([1, 2, 3]) - expect(isSubsetOf.call(a, b)).to.equal(true) - }) - - it('returns false when some elements are not in the other set', () => { - const a = new Set([1, 2, 4]) - const b = new Set([1, 2, 3]) - expect(isSubsetOf.call(a, b)).to.equal(false) - }) - - it('returns true for empty set', () => { - const a = new Set() - const b = new Set([1, 2]) - expect(isSubsetOf.call(a, b)).to.equal(true) - }) - }) - - describe('isSupersetOf', () => { - it('returns true when this set contains all elements of other', () => { - const a = new Set([1, 2, 3]) - const b = new Set([1, 2]) - expect(isSupersetOf.call(a, b)).to.equal(true) - }) - - it('returns false when other has elements not in this', () => { - const a = new Set([1, 2]) - const b = new Set([1, 2, 3]) - expect(isSupersetOf.call(a, b)).to.equal(false) - }) - - it('returns true when other is empty', () => { - const a = new Set([1, 2]) - expect(isSupersetOf.call(a, new Set())).to.equal(true) - }) - }) - - describe('isDisjointFrom', () => { - it('returns true when sets have no common elements', () => { - const a = new Set([1, 2]) - const b = new Set([3, 4]) - expect(isDisjointFrom.call(a, b)).to.equal(true) - }) - - it('returns false when sets share elements', () => { - const a = new Set([1, 2, 3]) - const b = new Set([3, 4]) - expect(isDisjointFrom.call(a, b)).to.equal(false) - }) - - it('returns true when either set is empty', () => { - const a = new Set([1, 2]) - expect(isDisjointFrom.call(a, new Set())).to.equal(true) - }) - }) -})