UNPKG

jsdom-testing-mocks

Version:

A set of tools for emulating browser behavior in jsdom environment

1,571 lines (1,560 loc) 120 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); // src/index.ts var index_exports = {}; __export(index_exports, { MockedCSS: () => MockedCSS, MockedCSSMathClamp: () => MockedCSSMathClamp, MockedCSSMathInvert: () => MockedCSSMathInvert, MockedCSSMathMax: () => MockedCSSMathMax, MockedCSSMathMin: () => MockedCSSMathMin, MockedCSSMathNegate: () => MockedCSSMathNegate, MockedCSSMathProduct: () => MockedCSSMathProduct, MockedCSSMathSum: () => MockedCSSMathSum, MockedCSSMathValue: () => MockedCSSMathValue, MockedCSSNumericValue: () => MockedCSSNumericValue, MockedCSSUnitValue: () => MockedCSSUnitValue, MockedDOMRect: () => MockedDOMRect, MockedDOMRectReadOnly: () => MockedDOMRectReadOnly, MockedIntersectionObserver: () => MockedIntersectionObserver, configMocks: () => configMocks, getConfig: () => getConfig, initCSSTypedOM: () => initCSSTypedOM, mockAnimationsApi: () => mockAnimationsApi, mockCSSTypedOM: () => initCSSTypedOM, mockDOMRect: () => mockDOMRect, mockElementBoundingClientRect: () => mockElementBoundingClientRect, mockIntersectionObserver: () => mockIntersectionObserver, mockResizeObserver: () => mockResizeObserver, mockViewport: () => mockViewport, mockViewportForTestGroup: () => mockViewportForTestGroup }); module.exports = __toCommonJS(index_exports); // src/helper.ts var UndefinedHookError = class extends Error { constructor(hook) { let message = ""; if (typeof (global == null ? void 0 : global["vi"]) !== "undefined") { message = `jsdom-testing-mocks: ${hook} is not defined. You can enable globals in your config or pass the hook manually to the configMocks function`; } else { message = `jsdom-testing-mocks: ${hook} is not defined. If you need to pass it manually, please use the configMocks function`; } super(message); } }; var WrongEnvironmentError = class extends Error { constructor() { super( "jsdom-testing-mocks: window is not defined. Please use this library in a browser environment" ); } }; var isJsdomEnv = () => { return typeof window !== "undefined"; }; // src/mocks/size/DOMRect.ts var protectedProps = ["_x", "_y", "_width", "_height"]; var MockedDOMRectReadOnly = class { constructor(x = 0, y = 0, width = 0, height = 0) { this._x = x; this._y = y; this._width = width; this._height = height; protectedProps.forEach((prop) => { const descriptor = Object.getOwnPropertyDescriptor(this, prop); if (descriptor) { Object.defineProperty(this, prop, __spreadProps(__spreadValues({}, descriptor), { enumerable: false })); } }); } get x() { return this._x; } set x(_value) { } get y() { return this._y; } set y(_value) { } get width() { return this._width; } set width(_value) { } get height() { return this._height; } set height(_value) { } get left() { return this._x; } set left(_value) { } get right() { return this._x + Math.max(0, this._width); } set right(_value) { } get top() { return this._y; } set top(_value) { } get bottom() { return this._y + Math.max(0, this._height); } set bottom(_value) { } toJSON() { return { bottom: this.bottom, height: this.height, left: this.left, right: this.right, top: this.top, width: this.width, x: this.x, y: this.y }; } toString() { return "[object DOMRectReadOnly]"; } }; var MockedDOMRect = class extends MockedDOMRectReadOnly { constructor(x = 0, y = 0, width = 0, height = 0) { super(x, y, width, height); } get x() { return super.x; } set x(_value) { this._x = _value; } get y() { return super.y; } set y(_value) { this._y = _value; } get width() { return super.width; } set width(_value) { this._width = _value; } get height() { return super.height; } set height(_value) { this._height = _value; } toString() { return "[object DOMRect]"; } }; function mockDOMRect() { if (!isJsdomEnv()) { throw new WrongEnvironmentError(); } Object.defineProperty(window, "DOMRectReadOnly", { writable: true, configurable: true, value: MockedDOMRectReadOnly }); Object.defineProperty(window, "DOMRect", { writable: true, configurable: true, value: MockedDOMRect }); } // src/tools.ts var getThrowHookError = (hookName) => () => { throw new UndefinedHookError(hookName); }; var config = { beforeAll: typeof beforeAll !== "undefined" ? beforeAll : getThrowHookError("beforeAll"), afterAll: typeof afterAll !== "undefined" ? afterAll : getThrowHookError("afterAll"), beforeEach: typeof beforeEach !== "undefined" ? beforeEach : getThrowHookError("beforeEach"), afterEach: typeof afterEach !== "undefined" ? afterEach : getThrowHookError("afterEach"), act: (trigger) => trigger() }; var getConfig = () => config; var configMocks = ({ beforeAll: beforeAll2, afterAll: afterAll2, beforeEach: beforeEach2, afterEach: afterEach2, act }) => { if (beforeAll2) config.beforeAll = beforeAll2; if (afterAll2) config.afterAll = afterAll2; if (beforeEach2) config.beforeEach = beforeEach2; if (afterEach2) config.afterEach = afterEach2; if (act) config.act = act; }; // src/mocks/intersection-observer.ts var config2 = getConfig(); var defaultState = { observers: [] }; var state = __spreadValues({}, defaultState); function isElement(tested) { return typeof HTMLElement === "object" ? tested instanceof HTMLElement : Boolean(tested) && typeof tested === "object" && tested !== null && tested.nodeType === 1 && typeof tested.nodeName === "string"; } function getObserversByNode(node) { return state.observers.filter((observer) => observer.nodes.includes(node)); } function normalizeNodeDescriptions(nodeDescriptions) { return nodeDescriptions.map((nodeDesc) => { if (isElement(nodeDesc)) { return { node: nodeDesc }; } return nodeDesc; }); } function getNodeDescriptionsByObserver(nodeDescriptions) { const observerNodes = []; nodeDescriptions.forEach(({ node, desc }) => { const observers = getObserversByNode(node); observers.forEach((observer) => { const observerNode = observerNodes.find( ({ observer: obs }) => obs === observer ); if (observerNode) { observerNode.nodeDescriptions.push({ node, desc }); } else { observerNodes.push({ observer, nodeDescriptions: [{ node, desc }] }); } }); }); return observerNodes; } function findNodeIndex(nodes, node) { const index = nodes.findIndex((nodeInArray) => node.isSameNode(nodeInArray)); if (index === -1) { throw new Error("IntersectionObserver mock: node not found"); } return index; } var MockedIntersectionObserver = class { constructor(callback, options) { this.nodes = []; this.nodeStates = []; this.root = null; this.rootMargin = "0px 0px 0px 0px"; this.thresholds = [0]; this.timeOrigin = 0; this.callback = callback; if (options) { if (typeof options.root !== "undefined") { this.root = options.root; } if (typeof options.rootMargin !== "undefined") { this.rootMargin = options.rootMargin; } if (typeof options.threshold !== "undefined") { this.thresholds = Array.isArray(options.threshold) ? options.threshold : [options.threshold]; } } this.timeOrigin = performance.now(); state.observers.push(this); } observe(node) { this.nodes.push(node); this.nodeStates.push({ isIntersecting: false, target: node, time: performance.now() - this.timeOrigin, rootBounds: new DOMRectReadOnly(), intersectionRect: new DOMRectReadOnly(), intersectionRatio: 0, boundingClientRect: new DOMRectReadOnly() }); } unobserve(node) { const index = this.nodes.findIndex((value) => value.isSameNode(node)); this.nodes.splice(index, 1); this.nodeStates.splice(index, 1); } disconnect() { this.nodes = []; this.nodeStates = []; } triggerNode(node, desc) { const index = findNodeIndex(this.nodes, node); const nodeState = this.nodeStates[index]; this.nodeStates[index] = __spreadValues(__spreadProps(__spreadValues({}, nodeState), { time: performance.now() - this.timeOrigin, target: node }), desc); this.callback([this.nodeStates[index]], this); } triggerNodes(nodeDescriptions) { if (nodeDescriptions.length === 0) return; const nodeIndexes = nodeDescriptions.map( ({ node }) => findNodeIndex(this.nodes, node) ); const nodeStates = nodeDescriptions.map(({ node, desc }, index) => { const newState = __spreadValues(__spreadProps(__spreadValues({}, this.nodeStates[nodeIndexes[index]]), { time: performance.now() - this.timeOrigin, target: node }), desc); this.nodeStates[nodeIndexes[index]] = newState; return newState; }); this.callback(nodeStates, this); } takeRecords() { return []; } }; function mockIntersectionObserver() { if (!isJsdomEnv()) { throw new WrongEnvironmentError(); } mockDOMRect(); const savedImplementation = window.IntersectionObserver; Object.defineProperty(window, "IntersectionObserver", { writable: true, configurable: true, value: MockedIntersectionObserver }); config2.afterAll(() => { window.IntersectionObserver = savedImplementation; }); return { enterAll: (desc) => { config2.act(() => { state.observers.forEach((observer) => { const nodeDescriptions = observer.nodes.map((node) => ({ node, desc: __spreadProps(__spreadValues({ intersectionRatio: 1 }, desc), { isIntersecting: true }) })); observer.triggerNodes(nodeDescriptions); }); }); }, enterNode: (node, desc) => { const observers = getObserversByNode(node); config2.act(() => { observers.forEach((observer) => { observer.triggerNode(node, __spreadProps(__spreadValues({ intersectionRatio: 1 }, desc), { isIntersecting: true })); }); }); }, enterNodes: (nodeDescriptions) => { const normalizedNodeDescriptions = normalizeNodeDescriptions(nodeDescriptions); const observerNodes = getNodeDescriptionsByObserver( normalizedNodeDescriptions ); config2.act(() => { observerNodes.forEach(({ observer, nodeDescriptions: nodeDescriptions2 }) => { observer.triggerNodes( nodeDescriptions2.map(({ node, desc }) => ({ node, desc: __spreadProps(__spreadValues({ intersectionRatio: 1 }, desc), { isIntersecting: true }) })) ); }); }); }, leaveAll: (desc) => { config2.act(() => { state.observers.forEach((observer) => { const nodeDescriptions = observer.nodes.map((node) => ({ node, desc: __spreadProps(__spreadValues({ intersectionRatio: 0 }, desc), { isIntersecting: false }) })); observer.triggerNodes(nodeDescriptions); }); }); }, leaveNode: (node, desc) => { const observers = getObserversByNode(node); config2.act(() => { observers.forEach((observer) => { observer.triggerNode(node, __spreadProps(__spreadValues({ intersectionRatio: 0 }, desc), { isIntersecting: false })); }); }); }, triggerNodes: (nodeDescriptions) => { const normalizedNodeDescriptions = normalizeNodeDescriptions(nodeDescriptions); const observerNodes = getNodeDescriptionsByObserver( normalizedNodeDescriptions ); config2.act(() => { observerNodes.forEach(({ observer, nodeDescriptions: nodeDescriptions2 }) => { observer.triggerNodes(nodeDescriptions2); }); }); }, leaveNodes: (nodeDescriptions) => { const normalizedNodeDescriptions = normalizeNodeDescriptions(nodeDescriptions); const observerNodes = getNodeDescriptionsByObserver( normalizedNodeDescriptions ); config2.act(() => { observerNodes.forEach(({ observer, nodeDescriptions: nodeDescriptions2 }) => { observer.triggerNodes( nodeDescriptions2.map(({ node, desc }) => ({ node, desc: __spreadProps(__spreadValues({ intersectionRatio: 0 }, desc), { isIntersecting: false }) })) ); }); }); }, cleanup: () => { window.IntersectionObserver = savedImplementation; state.observers = []; } }; } // src/mocks/resize-observer.ts var config3 = getConfig(); var state2 = { observers: [], targetObservers: /* @__PURE__ */ new Map(), elementSizes: /* @__PURE__ */ new Map() }; function resetState() { state2.observers = []; state2.targetObservers = /* @__PURE__ */ new Map(); state2.elementSizes = /* @__PURE__ */ new Map(); } function defineResizeObserverSize(input) { var _a, _b; return { blockSize: (_a = input.blockSize) != null ? _a : 0, inlineSize: (_b = input.inlineSize) != null ? _b : 0 }; } var MockedResizeObserver = class { constructor(callback) { this.observationTargets = /* @__PURE__ */ new Set(); this.activeTargets = /* @__PURE__ */ new Set(); this.observe = (node) => { this.observationTargets.add(node); this.activeTargets.add(node); if (state2.targetObservers.has(node)) { state2.targetObservers.get(node).push(this); } else { state2.targetObservers.set(node, [this]); } }; this.unobserve = (node) => { this.observationTargets.delete(node); const targetObservers = state2.targetObservers.get(node); if (targetObservers) { const index = targetObservers.findIndex((mro) => mro === this); targetObservers.splice(index, 1); if (targetObservers.length === 0) { state2.targetObservers.delete(node); } } }; this.disconnect = () => { for (const node of this.observationTargets) { const targetObservers = state2.targetObservers.get(node); if (targetObservers) { const index = targetObservers.findIndex((mro) => mro === this); targetObservers.splice(index, 1); if (targetObservers.length === 0) { state2.targetObservers.delete(node); } } } this.observationTargets.clear(); }; this.callback = callback; state2.observers.push(this); } }; function elementToEntry(element) { const boundingClientRect = element.getBoundingClientRect(); let sizes = state2.elementSizes.get(element); if (!sizes) { sizes = { borderBoxSize: [ { blockSize: boundingClientRect.width, inlineSize: boundingClientRect.height } ], contentBoxSize: [ { blockSize: boundingClientRect.width, inlineSize: boundingClientRect.height } ], contentRect: boundingClientRect }; } if (sizes.contentRect.width === 0 && sizes.contentRect.height === 0) { return null; } return { borderBoxSize: Object.freeze(sizes.borderBoxSize), contentBoxSize: Object.freeze(sizes.contentBoxSize), contentRect: sizes.contentRect, devicePixelContentBoxSize: Object.freeze( sizes.contentBoxSize.map((size) => ({ blockSize: size.blockSize * window.devicePixelRatio, inlineSize: size.inlineSize * window.devicePixelRatio })) ), target: element }; } function mockResizeObserver() { if (!isJsdomEnv()) { throw new WrongEnvironmentError(); } mockDOMRect(); const savedImplementation = window.ResizeObserver; Object.defineProperty(window, "ResizeObserver", { writable: true, configurable: true, value: MockedResizeObserver }); config3.afterEach(() => { resetState(); }); config3.afterAll(() => { window.ResizeObserver = savedImplementation; }); return { getObservers: (element) => { var _a; if (element) { return [...(_a = state2.targetObservers.get(element)) != null ? _a : []]; } return [...state2.observers]; }, getObservedElements: (observer) => { if (observer) { return [...observer.observationTargets]; } return [...state2.targetObservers.keys()]; }, mockElementSize: (element, size) => { let contentBoxSize; let borderBoxSize; if (!size.borderBoxSize && size.contentBoxSize) { if (!Array.isArray(size.contentBoxSize)) { size.contentBoxSize = [size.contentBoxSize]; } contentBoxSize = size.contentBoxSize.map(defineResizeObserverSize); borderBoxSize = contentBoxSize; } else if (size.borderBoxSize && !size.contentBoxSize) { if (!Array.isArray(size.borderBoxSize)) { size.borderBoxSize = [size.borderBoxSize]; } contentBoxSize = size.borderBoxSize.map(defineResizeObserverSize); borderBoxSize = contentBoxSize; } else if (size.borderBoxSize && size.contentBoxSize) { if (!Array.isArray(size.borderBoxSize)) { size.borderBoxSize = [size.borderBoxSize]; } if (!Array.isArray(size.contentBoxSize)) { size.contentBoxSize = [size.contentBoxSize]; } contentBoxSize = size.contentBoxSize.map(defineResizeObserverSize); borderBoxSize = size.borderBoxSize.map(defineResizeObserverSize); if (borderBoxSize.length !== contentBoxSize.length) { throw new Error( "Both borderBoxSize and contentBoxSize must have the same amount of elements." ); } } else { throw new Error( "Neither borderBoxSize nor contentBoxSize was provided." ); } contentBoxSize.forEach((size2, index) => { if (size2.blockSize < 0) { throw new Error( `contentBoxSize[${index}].blockSize must not be negative.` ); } if (size2.inlineSize < 0) { throw new Error( `contentBoxSize[${index}].inlineSize must not be negative.` ); } }); borderBoxSize.forEach((size2, index) => { if (size2.blockSize < 0) { throw new Error( `borderBoxSize[${index}].blockSize must not be negative.` ); } if (size2.inlineSize < 0) { throw new Error( `borderBoxSize[${index}].inlineSize must not be negative.` ); } }); const contentRect = new DOMRect( 0, 0, contentBoxSize.reduce((acc, size2) => acc + size2.inlineSize, 0), contentBoxSize.reduce((acc, size2) => acc + size2.blockSize, 0) ); state2.elementSizes.set(element, { contentBoxSize, borderBoxSize, contentRect }); }, resize: (elements = [], { ignoreImplicit = false } = {}) => { config3.act(() => { if (!Array.isArray(elements)) { elements = [elements]; } for (const observer of state2.observers) { const observedSubset = elements.filter( (element) => observer.observationTargets.has(element) ); const observedSubsetAndActive = /* @__PURE__ */ new Set([ ...observedSubset, ...ignoreImplicit ? [] : observer.activeTargets ]); observer.activeTargets.clear(); const entries = Array.from(observedSubsetAndActive).map(elementToEntry).filter(Boolean); if (entries.length > 0) { observer.callback(entries, observer); } } }); } }; } // src/mocks/size/size.ts var mockElementBoundingClientRect = (element, { x = 0, y = 0, width = 0, height = 0 }) => { mockDOMRect(); const savedImplementation = element.getBoundingClientRect; element.getBoundingClientRect = () => new DOMRectReadOnly(x, y, width, height); return savedImplementation; }; // src/mocks/viewport.ts var import_css_mediaquery = __toESM(require("css-mediaquery"), 1); // src/mocks/MediaQueryListEvent.ts var MockedMediaQueryListEvent = class extends Event { constructor(type, eventInitDict = {}) { var _a, _b; super(type); this.media = (_a = eventInitDict.media) != null ? _a : ""; this.matches = (_b = eventInitDict.matches) != null ? _b : false; } }; function mockMediaQueryListEvent() { if (!isJsdomEnv()) { throw new WrongEnvironmentError(); } if (typeof MediaQueryListEvent === "undefined") { Object.defineProperty(window, "MediaQueryListEvent", { writable: true, configurable: true, value: MockedMediaQueryListEvent }); } } // src/mocks/viewport.ts var config4 = getConfig(); function isEventListenerObject(obj) { return obj.handleEvent !== void 0; } function mockViewport(desc) { if (!isJsdomEnv()) { throw new WrongEnvironmentError(); } mockMediaQueryListEvent(); const state3 = { currentDesc: desc, oldListeners: [], listeners: [] }; const savedImplementation = window.matchMedia; const addOldListener = (list, matches, listener) => { state3.oldListeners.push({ listener, matches, list }); }; const removeOldListener = (listenerToRemove) => { const index = state3.oldListeners.findIndex( ({ listener }) => listener === listenerToRemove ); state3.oldListeners.splice(index, 1); }; const addListener = (list, matches, listener) => { state3.listeners.push({ listener, matches, list }); }; const removeListener = (listenerToRemove) => { const index = state3.listeners.findIndex( ({ listener }) => listener === listenerToRemove ); state3.listeners.splice(index, 1); }; Object.defineProperty(window, "matchMedia", { writable: true, value: (query) => ({ get matches() { return import_css_mediaquery.default.match(query, state3.currentDesc); }, media: query, onchange: null, addListener: function(listener) { if (listener) { addOldListener(this, this.matches, listener); } }, // deprecated removeListener: (listener) => { if (listener) { removeOldListener(listener); } }, // deprecated addEventListener: function(eventType, listener) { if (eventType === "change") { addListener(this, this.matches, listener); } }, removeEventListener: (eventType, listener) => { if (eventType === "change") { if (isEventListenerObject(listener)) { removeListener(listener.handleEvent); } else { removeListener(listener); } } }, dispatchEvent: (event) => { if (event.type === "change") { state3.listeners.forEach(({ listener, list }) => { if (isEventListenerObject(listener)) { listener.handleEvent(event); } else { listener.call(list, event); } }); state3.oldListeners.forEach(({ listener, list }) => { listener.call(list, event); }); } return true; } }) }); return { cleanup: () => { window.matchMedia = savedImplementation; }, set: (newDesc) => { config4.act(() => { state3.currentDesc = newDesc; state3.listeners.forEach( ({ listener, matches, list }, listenerIndex) => { const newMatches = list.matches; if (newMatches !== matches) { const changeEvent = new MediaQueryListEvent("change", { matches: newMatches, media: list.media }); if (isEventListenerObject(listener)) { listener.handleEvent(changeEvent); } else { listener.call(list, changeEvent); } state3.listeners[listenerIndex].matches = newMatches; } } ); state3.oldListeners.forEach( ({ listener, matches, list }, listenerIndex) => { const newMatches = list.matches; if (newMatches !== matches) { const changeEvent = new MediaQueryListEvent("change", { matches: newMatches, media: list.media }); listener.call(list, changeEvent); state3.oldListeners[listenerIndex].matches = newMatches; } } ); }); } }; } function mockViewportForTestGroup(desc) { let viewport; config4.beforeAll(() => { viewport = mockViewport(desc); }); config4.afterAll(() => { viewport.cleanup(); }); } // src/mocks/css-typed-om/index.ts var UNIT_CONVERSIONS = { // Length units (canonical: px) "px": { canonical: "px", factor: 1 }, "in": { canonical: "px", factor: 96 }, "cm": { canonical: "px", factor: 96 / 2.54 }, "mm": { canonical: "px", factor: 96 / 25.4 }, "pt": { canonical: "px", factor: 96 / 72 }, "pc": { canonical: "px", factor: 96 / 6 }, "Q": { canonical: "px", factor: 96 / 101.6 }, // Angle units (canonical: deg) "deg": { canonical: "deg", factor: 1 }, "rad": { canonical: "deg", factor: 180 / Math.PI }, "grad": { canonical: "deg", factor: 0.9 }, "turn": { canonical: "deg", factor: 360 }, // Time units (canonical: ms) "ms": { canonical: "ms", factor: 1 }, "s": { canonical: "ms", factor: 1e3 }, // Frequency units (canonical: Hz) "Hz": { canonical: "Hz", factor: 1 }, "kHz": { canonical: "Hz", factor: 1e3 }, // Resolution units (canonical: dppx) "dppx": { canonical: "dppx", factor: 1 }, "dpi": { canonical: "dppx", factor: 1 / 96 }, "dpcm": { canonical: "dppx", factor: 2.54 / 96 } }; function getBaseType(unit) { if (unit === "number") return null; if (unit === "percent") return "percent"; const lengthUnits = [ // Absolute "px", "in", "cm", "mm", "pt", "pc", "Q", // Font-relative "em", "rem", "ex", "ch", "cap", "ic", "lh", "rlh", // Viewport-relative (original) "vw", "vh", "vi", "vb", "vmin", "vmax", // Viewport-relative (small) "svw", "svh", "svi", "svb", "svmin", "svmax", // Viewport-relative (large) "lvw", "lvh", "lvi", "lvb", "lvmin", "lvmax", // Viewport-relative (dynamic) "dvw", "dvh", "dvi", "dvb", "dvmin", "dvmax", // Container query "cqw", "cqh", "cqi", "cqb", "cqmin", "cqmax" ]; if (lengthUnits.includes(unit)) return "length"; if (["deg", "rad", "grad", "turn"].includes(unit)) return "angle"; if (["ms", "s"].includes(unit)) return "time"; if (["Hz", "kHz"].includes(unit)) return "frequency"; if (["dppx", "dpi", "dpcm"].includes(unit)) return "resolution"; if (unit === "fr") return "flex"; return null; } function createTypeFromUnit(unit) { const baseType = getBaseType(unit); if (baseType && baseType !== "percent") { const result = {}; result[baseType] = 1; return result; } else if (baseType === "percent") { return { percent: 1 }; } return {}; } function addTypes(type1, type2) { const result = __spreadValues({}, type1); const isType1Number = Object.keys(type1).length === 0 || Object.values(type1).every((v) => v === 0 || v === void 0); const isType2Number = Object.keys(type2).length === 0 || Object.values(type2).every((v) => v === 0 || v === void 0); if (isType1Number && isType2Number) { return {}; } if (isType1Number || isType2Number) { return null; } const keys = ["length", "angle", "time", "frequency", "resolution", "flex", "percent"]; for (const key of keys) { if (key === "percentHint") continue; if ((type1[key] || 0) !== (type2[key] || 0)) { return null; } } return result; } function multiplyTypes(type1, type2) { const result = { length: type1.length + type2.length, angle: type1.angle + type2.angle, time: type1.time + type2.time, frequency: type1.frequency + type2.frequency, resolution: type1.resolution + type2.resolution, flex: type1.flex + type2.flex, percent: type1.percent + type2.percent }; return result; } function convertUnit(value, fromUnit, toUnit) { if (fromUnit === toUnit) return value; const fromConversion = UNIT_CONVERSIONS[fromUnit]; const toConversion = UNIT_CONVERSIONS[toUnit]; if (!fromConversion || !toConversion) return null; if (fromConversion.canonical !== toConversion.canonical) return null; const result = value * fromConversion.factor / toConversion.factor; return Math.round(result * 1e4) / 1e4; } function getCanonicalUnit(unit) { const conversion = UNIT_CONVERSIONS[unit]; return conversion ? conversion.canonical : unit; } function rectifyNumberish(value, allowDirectNumbers = false) { if (typeof value === "number") { if (allowDirectNumbers) { return new MockedCSSUnitValue(value, "number"); } throw new TypeError("Numbers cannot be used directly in CSS Typed OM operations. Use CSS.number() to create a CSSNumericValue."); } if (value instanceof MockedCSSNumericValue) { return value; } throw new TypeError("Invalid CSSNumberish value"); } var MockedCSSNumericValue = class _MockedCSSNumericValue { constructor() { if (new.target === _MockedCSSNumericValue) { throw new TypeError("Cannot instantiate abstract class MockedCSSNumericValue directly"); } } /** * Adds one or more values to this value */ add(...values) { if (values.length === 0) return this; if (values.some((v) => typeof v === "number")) { throw new TypeError("Cannot add numbers directly. Use CSS.number() instead."); } const operands = [this, ...values.map((v) => rectifyNumberish(v))]; if (operands.every((op) => op instanceof MockedCSSUnitValue)) { const unitValues = operands; const firstUnit = unitValues[0].unit; if (unitValues.every((uv) => uv.unit === firstUnit)) { const sum = unitValues.reduce((acc, uv) => acc + uv.value, 0); return new MockedCSSUnitValue(sum, firstUnit); } } if (operands.every((op) => op instanceof MockedCSSUnitValue && op.unit === "number")) { const sum = operands.reduce((acc, op) => acc + op.value, 0); return new MockedCSSUnitValue(sum, "number"); } let resultType = this.type(); for (let i = 1; i < operands.length; i++) { const opType = operands[i].type(); const addedType = addTypes(resultType, opType); if (addedType === null) { throw new TypeError("Incompatible types for addition"); } resultType = addedType; } return new MockedCSSMathSum(operands); } /** * Subtracts one or more values from this value */ sub(...values) { if (values.length === 0) return this; if (values.some((v) => typeof v === "number")) { throw new TypeError("Cannot subtract numbers directly. Use CSS.number() instead."); } const negatedValues = values.map((v) => { const numeric = rectifyNumberish(v); return numeric.negate(); }); return this.add(...negatedValues); } /** * Multiplies this value by one or more values */ mul(...values) { if (values.length === 0) return this; const operands = [this, ...values.map((v) => rectifyNumberish(v, true))]; if (operands.every((op) => op instanceof MockedCSSUnitValue)) { const unitValues = operands; const numbers = unitValues.filter((uv) => uv.unit === "number"); const nonNumbers = unitValues.filter((uv) => uv.unit !== "number"); if (nonNumbers.length <= 1) { const numberProduct = numbers.reduce((acc, uv) => acc * uv.value, 1); if (nonNumbers.length === 0) { return new MockedCSSUnitValue(numberProduct, "number"); } else { const singleUnit = nonNumbers[0]; return new MockedCSSUnitValue(numberProduct * singleUnit.value, singleUnit.unit); } } } return new MockedCSSMathProduct(operands); } /** * Divides this value by one or more values */ div(...values) { if (values.length === 0) return this; const invertedValues = values.map((v) => { const numeric = rectifyNumberish(v, true); if (numeric instanceof MockedCSSUnitValue && numeric.value === 0) { throw new RangeError("Cannot divide by zero"); } return numeric.invert(); }); return this.mul(...invertedValues); } /** * Returns the minimum of this value and one or more other values */ min(...values) { if (values.length === 0) return this; if (values.some((v) => typeof v === "number")) { throw new TypeError("Cannot use raw numbers in min(). Use CSS.number() instead."); } const operands = [this, ...values.map((v) => rectifyNumberish(v))]; if (operands.every((op) => op instanceof MockedCSSUnitValue)) { const unitValues = operands; const firstUnit = unitValues[0].unit; if (unitValues.every((uv) => uv.unit === firstUnit)) { const minValue = Math.min(...unitValues.map((uv) => uv.value)); return new MockedCSSUnitValue(minValue, firstUnit); } } return new MockedCSSMathMin(operands); } /** * Returns the maximum of this value and one or more other values */ max(...values) { if (values.length === 0) return this; if (values.some((v) => typeof v === "number")) { throw new TypeError("Cannot use raw numbers in max(). Use CSS.number() instead."); } const operands = [this, ...values.map((v) => rectifyNumberish(v))]; if (operands.every((op) => op instanceof MockedCSSUnitValue)) { const unitValues = operands; const firstUnit = unitValues[0].unit; if (unitValues.every((uv) => uv.unit === firstUnit)) { const maxValue = Math.max(...unitValues.map((uv) => uv.value)); return new MockedCSSUnitValue(maxValue, firstUnit); } } return new MockedCSSMathMax(operands); } /** * Checks if this value equals one or more other values */ equals(...values) { for (const value of values) { const numeric = rectifyNumberish(value); if (!this.isEqualTo(numeric)) { return false; } } return true; } /** * Converts this value to the specified unit */ to(unit) { if (!(this instanceof MockedCSSUnitValue)) { throw new TypeError("Cannot convert complex values to a single unit"); } const converted = convertUnit(this.value, this.unit, unit); if (converted === null) { throw new TypeError(`Cannot convert from '${this.unit}' to '${unit}'`); } return new MockedCSSUnitValue(converted, unit); } /** * Converts this value to a sum of the specified units */ toSum(...units) { var _a; if (units.length === 0) { if (this instanceof MockedCSSMathSum) { return this; } return new MockedCSSMathSum([this]); } if (this instanceof MockedCSSUnitValue) { const results = []; const sourceUnit = this.unit; let foundCompatible = false; for (const targetUnit of units) { const converted = convertUnit(this.value, sourceUnit, targetUnit); if (converted !== null && !foundCompatible) { results.push(new MockedCSSUnitValue(converted, targetUnit)); foundCompatible = true; } else { results.push(new MockedCSSUnitValue(0, targetUnit)); } } if (!foundCompatible) { throw new TypeError(`Cannot convert ${sourceUnit} to any of the specified units`); } return new MockedCSSMathSum(results); } if (this instanceof MockedCSSMathSum) { const groups = /* @__PURE__ */ new Map(); for (const value of this.values) { if (value instanceof MockedCSSUnitValue) { const key = getCanonicalUnit(value.unit); if (!groups.has(key)) { groups.set(key, []); } (_a = groups.get(key)) == null ? void 0 : _a.push(value); } } const results = []; for (const targetUnit of units) { const canonicalUnit = getCanonicalUnit(targetUnit); const compatibleValues = groups.get(canonicalUnit) || []; if (compatibleValues.length > 0) { let sum = 0; for (const val of compatibleValues) { const converted = convertUnit(val.value, val.unit, targetUnit); if (converted !== null) { sum += converted; } } results.push(new MockedCSSUnitValue(sum, targetUnit)); groups.delete(canonicalUnit); } else { results.push(new MockedCSSUnitValue(0, targetUnit)); } } if (groups.size > 0) { throw new TypeError("Some values cannot be converted to the specified units"); } return new MockedCSSMathSum(results); } throw new TypeError("Cannot convert complex math expressions to sum"); } /** * Negates this value */ negate() { if (this instanceof MockedCSSUnitValue) { return new MockedCSSUnitValue(-this.value, this.unit); } return new MockedCSSMathNegate(this); } /** * Inverts this value (1/value) */ invert() { if (this instanceof MockedCSSUnitValue && this.unit === "number") { if (this.value === 0) { throw new RangeError("Cannot invert zero"); } return new MockedCSSUnitValue(1 / this.value, "number"); } return new MockedCSSMathInvert(this); } /** * Checks if this value is equal to another */ isEqualTo(other) { if (this.constructor !== other.constructor) { return false; } if (this instanceof MockedCSSUnitValue && other instanceof MockedCSSUnitValue) { return this.value === other.value && this.unit === other.unit; } if (this instanceof MockedCSSMathSum && other instanceof MockedCSSMathSum) { return this.values.length === other.values.length && this.values.every((v, i) => v.isEqualTo(other.values[i])); } return false; } // eslint-disable-next-line @typescript-eslint/no-unused-vars static parse(_cssText) { throw new TypeError("CSS.parse is not available in browsers"); } }; var MockedCSSUnitValue = class extends MockedCSSNumericValue { constructor(value, unit) { super(); this.value = value; this.unit = unit; if (typeof value !== "number" || !isFinite(value)) { throw new TypeError("Invalid numeric value"); } if (unit === "%") { this.unit = "percent"; } if (this.unit !== "number" && this.unit !== "percent" && getBaseType(this.unit) === null) { throw new TypeError(`Invalid unit: ${unit}`); } } type() { return createTypeFromUnit(this.unit); } toString() { if (this.unit === "number") { return String(this.value); } if (Math.abs(this.value) >= 1e6) { const exp = this.value.toExponential(); const formatted = exp.replace(/e\+(\d)$/, "e+0$1"); return `${formatted}${this.unit}`; } return `${this.value}${this.unit}`; } // eslint-disable-next-line @typescript-eslint/no-unused-vars static parse(_cssText) { throw new TypeError("CSS.parse is not available in browsers"); } }; var MockedCSSMathValue = class extends MockedCSSNumericValue { toString() { return `calc(${this.toCSSString()})`; } }; var MockedCSSMathSum = class extends MockedCSSMathValue { constructor(values) { super(); this.values = values; this.operator = "sum"; if (values.length === 0) { throw new SyntaxError("CSSMathSum requires at least one value"); } } type() { let result = this.values[0].type(); for (let i = 1; i < this.values.length; i++) { const addedType = addTypes(result, this.values[i].type()); if (addedType === null) { throw new TypeError("Incompatible types in sum"); } result = addedType; } return result; } toCSSString() { return this.values.map((v, i) => { const str = v.toString(); if (i > 0) { if (str.startsWith("-")) { return `+ ${str}`; } else { return `+ ${str}`; } } return str; }).join(" "); } }; var MockedCSSMathProduct = class extends MockedCSSMathValue { constructor(values) { super(); this.values = values; this.operator = "product"; if (values.length === 0) { throw new SyntaxError("CSSMathProduct requires at least one value"); } } type() { let result = this.values[0].type(); for (let i = 1; i < this.values.length; i++) { result = multiplyTypes(result, this.values[i].type()); } return result; } toCSSString() { return this.values.map((v) => v.toString()).join(" * "); } }; var MockedCSSMathNegate = class extends MockedCSSMathValue { constructor(value) { super(); this.value = value; this.operator = "negate"; } type() { return this.value.type(); } toCSSString() { return `-${this.value.toString()}`; } }; var MockedCSSMathInvert = class extends MockedCSSMathValue { constructor(value) { super(); this.value = value; this.operator = "invert"; } type() { const valueType = this.value.type(); const result = { length: -valueType.length, angle: -valueType.angle, time: -valueType.time, frequency: -valueType.frequency, resolution: -valueType.resolution, flex: -valueType.flex, percent: -valueType.percent }; return result; } toCSSString() { return `1 / ${this.value.toString()}`; } }; var MockedCSSMathMin = class extends MockedCSSMathValue { constructor(values) { super(); this.values = values; this.operator = "min"; if (values.length === 0) { throw new SyntaxError("CSSMathMin requires at least one value"); } } type() { let result = this.values[0].type(); for (let i = 1; i < this.values.length; i++) { const addedType = addTypes(result, this.values[i].type()); if (addedType === null) { throw new TypeError("Incompatible types in min()"); } result = addedType; } return result; } toCSSString() { return `min(${this.values.map((v) => v.toString()).join(", ")})`; } toString() { return this.toCSSString(); } }; var MockedCSSMathMax = class extends MockedCSSMathValue { constructor(values) { super(); this.values = values; this.operator = "max"; if (values.length === 0) { throw new SyntaxError("CSSMathMax requires at least one value"); } } type() { let result = this.values[0].type(); for (let i = 1; i < this.values.length; i++) { const addedType = addTypes(result, this.values[i].type()); if (addedType === null) { throw new TypeError("Incompatible types in max()"); } result = addedType; } return result; } toCSSString() { return `max(${this.values.map((v) => v.toString()).join(", ")})`; } toString() { return this.toCSSString(); } }; var MockedCSSMathClamp = class extends MockedCSSMathValue { constructor(lower, value, upper) { super(); this.lower = lower; this.value = value; this.upper = upper; this.operator = "clamp"; const lowerType = lower.type(); const valueType = value.type(); const upperType = upper.type(); const resultType = addTypes(lowerType, valueType); if (resultType === null) { throw new TypeError("Incompatible types in clamp() lower and value"); } const finalType = addTypes(resultType, upperType); if (finalType === null) { throw new TypeError("Incompatible types in clamp()"); } } type() { return this.lower.type(); } toCSSString() { return `clamp(${this.lower.toString()}, ${this.value.toString()}, ${this.upper.toString()})`; } toString() { return this.toCSSString(); } }; var MockedCSS = { // Numbers and percentages number: (value) => new MockedCSSUnitValue(value, "number"), percent: (value) => new MockedCSSUnitValue(value, "percent"), // Length units - Absolute px: (value) => new MockedCSSUnitValue(value, "px"), cm: (value) => new MockedCSSUnitValue(value, "cm"), mm: (value) => new MockedCSSUnitValue(value, "mm"), in: (value) => new MockedCSSUnitValue(value, "in"), pt: (value) => new MockedCSSUnitValue(value, "pt"), pc: (value) => new MockedCSSUnitValue(value, "pc"), Q: (value) => new MockedCSSUnitValue(value, "Q"), // Length units - Font-relative em: (value) => new MockedCSSUnitValue(value, "em"), rem: (value) => new MockedCSSUnitValue(value, "rem"), ex: (value) => new MockedCSSUnitValue(value, "ex"), ch: (value) => new MockedCSSUnitValue(value, "ch"), cap: (value) => new MockedCSSUnitValue(value, "cap"), ic: (value) => new MockedCSSUnitValue(value, "ic"), lh: (value) => new MockedCSSUnitValue(value, "lh"), rlh: (value) => new MockedCSSUnitValue(value, "rlh"), // Length units - Viewport-relative (original) vw: (value) => new MockedCSSUnitValue(value, "vw"), vh: (value) => new MockedCSSUnitValue(value, "vh"), vi: (value) => new MockedCSSUnitValue(value, "vi"), vb: (value) => new MockedCSSUnitValue(value, "vb"), vmin: (value) => new MockedCSSUnitValue(value, "vmin"), vmax: (value) => new MockedCSSUnitValue(value, "vmax"), // Length units - Viewport-relative (small) svw: (value) => new MockedCSSUnitValue(value, "svw"), svh: (value) => new MockedCSSUnitValue(value, "svh"), svi: (value) => new MockedCSSUnitValue(value, "svi"), svb: (value) => new MockedCSSUnitValue(value, "svb"), svmin: (value) => new MockedCSSUnitValue(value, "svmin"), svmax: (value) => new MockedCSSUnitValue(value, "svmax"), // Length units - Viewport-relative (large) lvw: (value) => new MockedCSSUnitValue(value, "lvw"), lvh: (value) => new MockedCSSUnitValue(value, "lvh"), lvi: (value) => new MockedCSSUnitValue(value, "lvi"), lvb: (value) => new MockedCSSUnitValue(value, "lvb"), lvmin: (value) => new MockedCSSUnitValue(value, "lvmin"), lvmax: (value) => new MockedCSSUnitValue(value, "lvmax"), // Length units - Viewport-relative (dynamic) dvw: (value) => new Moc