jsdom-testing-mocks
Version:
A set of tools for emulating browser behavior in jsdom environment
1,571 lines (1,560 loc) • 120 kB
JavaScript
"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