Ctrl K
ComunidadGuías…

frontend/jest.polyfills.js

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// MSW v2 intercepts requests via spec-compliant fetch primitives that jsdom does
// not provide. Source them from undici (jsdom otherwise clobbers the Node globals)
// and assign before the test framework boots, so this must run first in setupFiles.
/* eslint-disable @typescript-eslint/no-require-imports */
const { TextEncoder, TextDecoder } = require('node:util')
const { ReadableStream, WritableStream, TransformStream } = require('node:stream/web')
const { Blob, File } = require('node:buffer')
const { BroadcastChannel, MessageChannel, MessagePort } = require('node:worker_threads')

// configurable so @mswjs/interceptors can wrap Request/Response when the server starts.
const define = (props) =>
    Object.defineProperties(
        globalThis,
        Object.fromEntries(
            Object.entries(props).map(([key, value]) => [key, { value, writable: true, configurable: true }])
        )
    )

// undici references TextEncoder/streams at module-eval time, so define these first.
define({
    TextEncoder,
    TextDecoder,
    ReadableStream,
    WritableStream,
    TransformStream,
    Blob,
    File,
    BroadcastChannel,
    MessageChannel,
    MessagePort,
})

// jsdom's setTimeout returns a number, but undici's internal fast-timer calls `.refresh()`/`.unref()`
// on the returned handle. Wrap jsdom's timer (so it still tears down with the window — Node's would
// leak past teardown) and expose a refreshable handle. Jest fake timers override these per-test.
const jsdomSetTimeout = globalThis.setTimeout
const jsdomClearTimeout = globalThis.clearTimeout
const handleId = (handle) => (handle && typeof handle === 'object' ? handle.__id : handle)
define({
    setTimeout: (callback, delay, ...args) => {
        const handle = {
            refresh() {
                jsdomClearTimeout(this.__id)
                this.__id = jsdomSetTimeout(callback, delay, ...args)
                return this
            },
            unref() {
                return this
            },
            ref() {
                return this
            },
            [Symbol.toPrimitive]() {
                return this.__id
            },
        }
        handle.__id = jsdomSetTimeout(callback, delay, ...args)
        return handle
    },
    clearTimeout: (handle) => jsdomClearTimeout(handleId(handle)),
})

// jsdom lacks setImmediate/clearImmediate, which undici needs. Node's setImmediate fires on the
// libuv check phase, which keeps running after jsdom tears down the window — and React 18's
// scheduler prefers setImmediate for performWorkUntilDeadline, so its render work lands
// post-teardown and throws "The `document` global ... is not defined anymore", crashing the whole
// file. Route it through the jsdom-bound setTimeout above so it tears down with the window instead.
define({
    setImmediate: (callback, ...args) => globalThis.setTimeout(callback, 0, ...args),
    clearImmediate: (handle) => globalThis.clearTimeout(handle),
})

// undici's fetch calls performance.markResourceTiming, which jsdom's performance lacks.
if (typeof globalThis.performance?.markResourceTiming !== 'function') {
    if (!globalThis.performance) {
        globalThis.performance = require('node:perf_hooks').performance
    }
    globalThis.performance.markResourceTiming = () => {}
}

const { fetch, Request, Response, Headers, FormData } = require('undici')

define({ fetch, Request, Response, Headers, FormData })

// jsdom ships no DOMRect, which Radix's ContextMenu constructs to anchor its content at the cursor.
// Without it, right-clicking a trigger under jest throws before the menu ever mounts.
if (typeof globalThis.DOMRect !== 'function') {
    class DOMRect {
        constructor(x = 0, y = 0, width = 0, height = 0) {
            this.x = x
            this.y = y
            this.width = width
            this.height = height
        }
        get top() {
            return this.height < 0 ? this.y + this.height : this.y
        }
        get bottom() {
            return this.height < 0 ? this.y : this.y + this.height
        }
        get left() {
            return this.width < 0 ? this.x + this.width : this.x
        }
        get right() {
            return this.width < 0 ? this.x : this.x + this.width
        }
        static fromRect(rect = {}) {
            return new DOMRect(rect.x, rect.y, rect.width, rect.height)
        }
        toJSON() {
            const { x, y, width, height, top, right, bottom, left } = this
            return { x, y, width, height, top, right, bottom, left }
        }
    }
    define({ DOMRect })
}