import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import { installArrayAtPolyfill, installObjectHasOwnPolyfill, installPromiseWithResolversPolyfill, installStringAtPolyfill, installStructuredClonePolyfill, installUrlCanParsePolyfill, } from './polyfills'; const descriptors = new Map< string, [object, PropertyKey, PropertyDescriptor | undefined] >(); function remember(key: string, target: object, property: PropertyKey) { descriptors.set(key, [ target, property, Object.getOwnPropertyDescriptor(target, property), ]); } function remove(target: object, property: PropertyKey) { Reflect.deleteProperty(target, property); } beforeEach(() => { remember('arrayAt', Array.prototype, 'at'); remember('stringAt', String.prototype, 'at'); remember('hasOwn', Object, 'hasOwn'); remember('withResolvers', Promise, 'withResolvers'); remember('structuredClone', globalThis, 'structuredClone'); remember('canParse', URL, 'canParse'); }); afterEach(() => { for (const [target, property, descriptor] of descriptors.values()) { if (descriptor) Object.defineProperty(target, property, descriptor); else remove(target, property); } descriptors.clear(); }); describe('chrome 90 browser polyfills', () => { it('installs Array.prototype.at and String.prototype.at', () => { remove(Array.prototype, 'at'); remove(String.prototype, 'at'); installArrayAtPolyfill(); installStringAtPolyfill(); expect(['first', 'last'].at(-1)).toBe('last'); expect('EasyFlow'.at(-1)).toBe('w'); expect('EasyFlow'.at(99)).toBeUndefined(); }); it('installs Object.hasOwn without accepting inherited properties', () => { remove(Object, 'hasOwn'); installObjectHasOwnPolyfill(); const value = Object.create({ inherited: true }) as { own?: boolean }; value.own = true; expect(Object.hasOwn(value, 'own')).toBe(true); expect(Object.hasOwn(value, 'inherited')).toBe(false); }); it('installs Promise.withResolvers and exposes working callbacks', async () => { remove(Promise, 'withResolvers'); installPromiseWithResolversPolyfill(); // eslint-disable-next-line n/no-unsupported-features/es-syntax -- 测试目标就是验证该 API 的兼容实现。 const { promise, resolve } = Promise.withResolvers(); resolve('ready'); await expect(promise).resolves.toBe('ready'); }); it('installs structuredClone with complex value and cycle support', () => { remove(globalThis, 'structuredClone'); installStructuredClonePolyfill(); const source: { createdAt: Date; lookup: Map; self?: unknown; } = { createdAt: new Date('2026-07-15T00:00:00.000Z'), lookup: new Map([['answer', 2026]]), }; source.self = source; const clone = structuredClone(source); expect(clone).not.toBe(source); expect(clone.self).toBe(clone); expect(clone.createdAt).toEqual(source.createdAt); expect(clone.lookup.get('answer')).toBe(2026); }); it('installs URL.canParse for absolute and base-relative URLs', () => { remove(URL, 'canParse'); installUrlCanParsePolyfill(); expect(URL.canParse('https://easyflow.example/chat')).toBe(true); expect(URL.canParse('/chat', 'https://easyflow.example')).toBe(true); expect(URL.canParse('/chat')).toBe(false); }); });