diff --git a/superset-frontend/jest.config.js b/superset-frontend/jest.config.js index 19106b124be..2ecb840278c 100644 --- a/superset-frontend/jest.config.js +++ b/superset-frontend/jest.config.js @@ -43,7 +43,7 @@ module.exports = { '/plugins/.*/esm', '/plugins/.*/lib', ], - setupFilesAfterEnv: ['/spec/helpers/setup.ts'], + setupFilesAfterEnv: ['/spec/helpers/setup.jest.ts'], snapshotSerializers: ['@emotion/jest/serializer'], testEnvironmentOptions: { globalsCleanup: true, diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 6dc1c366449..61411770d42 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -198,7 +198,6 @@ "@testing-library/user-event": "^12.8.3", "@types/content-disposition": "^0.5.9", "@types/dom-to-image": "^2.6.7", - "@types/jest": "^30.0.0", "@types/js-levenshtein": "^1.1.3", "@types/json-bigint": "^1.0.4", "@types/mousetrap": "^1.6.15", @@ -288,6 +287,7 @@ "tsx": "^4.21.0", "typescript": "5.4.5", "unzipper": "^0.12.3", + "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.0.18", "vm-browserify": "^1.1.2", "wait-on": "^9.0.4", @@ -3171,6 +3171,33 @@ "react": ">=16.8.0" } }, + "node_modules/@edge-runtime/primitives": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/primitives/-/primitives-6.0.0.tgz", + "integrity": "sha512-FqoxaBT+prPBHBwE1WXS1ocnu/VLTQyZ6NMUBAdbP7N2hsFTTxMC/jMu2D/8GAlMQfxeuppcPuCUk/HO3fpIvA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@edge-runtime/vm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@edge-runtime/vm/-/vm-5.0.0.tgz", + "integrity": "sha512-NKBGBSIKUG584qrS1tyxVpX/AKJKQw5HgjYEnPLC0QsTw79JrGn+qUr8CXFb955Iy7GUdiiUv1rJ6JBGvaKb6w==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@edge-runtime/primitives": "6.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -4450,45 +4477,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/console/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/console/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4506,86 +4494,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/console/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/console/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/console/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/console/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -4644,6 +4552,40 @@ } } }, + "node_modules/@jest/core/node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core/node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/core/node_modules/@jest/schemas": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", @@ -4657,32 +4599,23 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/core/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, + "node_modules/@jest/core/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, "node_modules/@jest/core/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4700,56 +4633,86 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/core/node_modules/jest-message-util": { + "node_modules/@jest/core/node_modules/jest-environment-node": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/core/node_modules/jest-util": { + "node_modules/@jest/core/node_modules/jest-leak-detector": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core/node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "jest-util": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/core/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/@jest/core/node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/core/node_modules/pretty-format": { @@ -4790,6 +4753,27 @@ "node": ">=8" } }, + "node_modules/@jest/core/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@jest/core/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/@jest/create-cache-key-function": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-30.2.0.tgz", @@ -4803,62 +4787,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/create-cache-key-function/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/create-cache-key-function/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/create-cache-key-function/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/create-cache-key-function/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/@jest/diff-sequences": { "version": "30.0.1", "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", @@ -4885,18 +4813,53 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "node_modules/@jest/environment/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "expect": "30.2.0", - "jest-snapshot": "30.2.0" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { @@ -4912,57 +4875,67 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "node_modules/@jest/expect/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0" + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "node_modules/@jest/expect/node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "license": "MIT" + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/@jest/expect/node_modules/chalk": { "version": "4.1.2", @@ -4981,136 +4954,201 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/expect/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "node_modules/@jest/expect/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/expect/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "node_modules/@jest/expect/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/@jest/expect/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "node_modules/@jest/expect/node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@jest/expect/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, "node_modules/@jest/expect/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "stack-utils": "^2.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "node_modules/@jest/expect/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/expect/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/@jest/expect/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/@jest/expect/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect/node_modules/pretty-format/node_modules/ansi-styles": { @@ -5136,6 +5174,20 @@ "node": ">=8" } }, + "node_modules/@jest/expect/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@jest/fake-timers": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", @@ -5154,6 +5206,134 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/fake-timers/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/fake-timers/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/fake-timers/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jest/get-type": { "version": "30.1.0", "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", @@ -5196,6 +5376,33 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/@jest/globals/node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals/node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/@jest/globals/node_modules/@jest/fake-timers": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", @@ -5227,29 +5434,10 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/@jest/globals/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -5280,22 +5468,51 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/globals/node_modules/jest-message-util": { + "node_modules/@jest/globals/node_modules/expect": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals/node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -5316,37 +5533,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@jest/globals/node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -5375,16 +5561,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/globals/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/pattern": { "version": "30.0.1", "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", @@ -5442,45 +5618,6 @@ } } }, - "node_modules/@jest/reporters/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/reporters/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -5512,6 +5649,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -5544,86 +5682,14 @@ "node": ">=10" } }, - "node_modules/@jest/reporters/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -5632,47 +5698,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@jest/reporters/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/reporters/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/@jest/reporters/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -5712,45 +5737,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/snapshot-utils/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/@jest/snapshot-utils/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5799,62 +5785,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/test-result/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/test-result/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/@jest/test-sequencer": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.2.0.tgz", @@ -5908,20 +5838,34 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/transform/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@jest/transform/node_modules/@jest/types": { + "node_modules/@jest/transform/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", @@ -5940,88 +5884,25 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/transform/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@jest/types/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "@sinclair/typebox": "^0.34.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/@jest/transform/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/@jest/types/node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@jest/transform/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } + "license": "MIT" }, "node_modules/@jest/types/node_modules/chalk": { "version": "4.1.2", @@ -6582,13 +6463,6 @@ "node": ">=10.13.0" } }, - "node_modules/@lerna/create/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/@lerna/create/node_modules/inquirer": { "version": "8.2.7", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", @@ -8108,44 +7982,6 @@ "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/agent/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@npmcli/agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/@npmcli/agent/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -12450,20 +12286,6 @@ } } }, - "node_modules/@storybook/test-runner/node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@storybook/test-runner/node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", @@ -12598,6 +12420,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@storybook/test-runner/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@storybook/test-runner/node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -12765,21 +12605,6 @@ "wrap-ansi": "^6.2.0" } }, - "node_modules/@storybook/test-runner/node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/@storybook/test-runner/node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -12868,38 +12693,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@storybook/test-runner/node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@storybook/test-runner/node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", @@ -12980,54 +12773,6 @@ } } }, - "node_modules/@storybook/test-runner/node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@storybook/test-runner/node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@storybook/test-runner/node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@storybook/test-runner/node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", @@ -13054,15 +12799,22 @@ "fsevents": "^2.3.2" } }, - "node_modules/@storybook/test-runner/node_modules/jest-leak-detector": { + "node_modules/@storybook/test-runner/node_modules/jest-message-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -13135,39 +12887,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@storybook/test-runner/node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/@storybook/test-runner/node_modules/jest-runtime": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", @@ -13234,6 +12953,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@storybook/test-runner/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@storybook/test-runner/node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", @@ -13331,13 +13068,13 @@ } }, "node_modules/@storybook/test-runner/node_modules/jest-watch-typeahead/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -13366,6 +13103,38 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@storybook/test-runner/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@storybook/test-runner/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/@storybook/test-runner/node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -13573,23 +13342,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@storybook/test-runner/node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/@storybook/test-runner/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -13617,27 +13369,6 @@ "node": ">=8" } }, - "node_modules/@storybook/test-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@storybook/test-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/@storybook/test-runner/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -15260,241 +14991,6 @@ "@types/istanbul-lib-report": "*" } }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/expect-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", - "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/expect": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", - "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.2.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@types/jquery": { "version": "3.5.33", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", @@ -16123,6 +15619,25 @@ "source-map": "^0.7.3" } }, + "node_modules/@types/whatwg-mimetype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz", + "integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -18395,6 +17910,30 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -18405,6 +17944,30 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk/node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/add-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", @@ -18442,16 +18005,12 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" } }, "node_modules/aggregate-error": { @@ -20750,18 +20309,6 @@ "node": ">=12" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -20840,9 +20387,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, "license": "MIT" }, @@ -20925,19 +20472,6 @@ "node": ">=8.0.0" } }, - "node_modules/columnify/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -21195,12 +20729,6 @@ "proto-list": "~1.2.1" } }, - "node_modules/config-chain/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, "node_modules/connect-history-api-fallback": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", @@ -21663,51 +21191,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/create-jest/node_modules/@jest/test-result": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", @@ -21767,6 +21250,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-jest/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/create-jest/node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -21872,21 +21373,6 @@ "node": ">=8" } }, - "node_modules/create-jest/node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/create-jest/node_modules/istanbul-lib-instrument": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", @@ -21904,38 +21390,6 @@ "node": ">=8" } }, - "node_modules/create-jest/node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/create-jest/node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", @@ -21982,54 +21436,6 @@ } } }, - "node_modules/create-jest/node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/create-jest/node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", @@ -22056,15 +21462,22 @@ "fsevents": "^2.3.2" } }, - "node_modules/create-jest/node_modules/jest-leak-detector": { + "node_modules/create-jest/node_modules/jest-message-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -22101,118 +21514,24 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/jest-runner": { + "node_modules/create-jest/node_modules/jest-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "emittery": "^0.13.1", + "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "picomatch": "^2.2.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/create-jest/node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", @@ -22231,26 +21550,38 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/jest-watcher": { + "node_modules/create-jest/node_modules/jest-worker": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", "jest-util": "^29.7.0", - "string-length": "^4.0.1" + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/create-jest/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/create-jest/node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -22279,23 +21610,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/create-jest/node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/create-jest/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -22316,27 +21630,6 @@ "node": ">=8" } }, - "node_modules/create-jest/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/create-jest/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/create-jest/node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", @@ -22537,139 +21830,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/css-minimizer-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -22867,6 +22027,13 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, "node_modules/cssstyle": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-6.0.1.tgz", @@ -24155,6 +23322,20 @@ ], "license": "BSD-2-Clause" }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", @@ -24582,20 +23763,6 @@ "node": ">=8.6" } }, - "node_modules/enquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -25506,22 +24673,6 @@ } } }, - "node_modules/eslint-plugin-prettier/node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, "node_modules/eslint-plugin-react": { "version": "7.37.5", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", @@ -26033,19 +25184,6 @@ "dev": true, "license": "MIT" }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -26319,6 +25457,134 @@ "node": ">=12.0.0" } }, + "node_modules/expect/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expect/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/expect/node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/expect/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/exponential-backoff": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", @@ -27568,15 +26834,6 @@ "node": ">=18" } }, - "node_modules/gaxios/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/gaxios/node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -27586,19 +26843,6 @@ "node": ">= 12" } }, - "node_modules/gaxios/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/gaxios/node_modules/node-fetch": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", @@ -28591,13 +27835,6 @@ "ini": "^1.3.2" } }, - "node_modules/gitconfiglocal/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/github-username": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/github-username/-/github-username-9.0.0.tgz", @@ -28727,13 +27964,6 @@ "node": ">=0.10.0" } }, - "node_modules/global-prefix/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/global-prefix/node_modules/is-windows": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", @@ -28804,6 +28034,13 @@ "node": ">=8" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, "node_modules/google-auth-library": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.2.0.tgz", @@ -28970,6 +28207,53 @@ "node": ">=0.10.0" } }, + "node_modules/happy-dom": { + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.7.0.tgz", + "integrity": "sha512-hR/uLYQdngTyEfxnOoa+e6KTcfBFyc1hgFj/Cc144A5JJUuHFYqIEBDcD4FeGqUeKLRZqJ9eN9u7/GDjYEgS1g==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@types/node": ">=20.0.0", + "@types/whatwg-mimetype": "^3.0.2", + "@types/ws": "^8.18.1", + "entities": "^7.0.1", + "whatwg-mimetype": "^3.0.0", + "ws": "^8.18.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/happy-dom/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/happy-dom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -29765,18 +29049,17 @@ } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http-proxy-middleware": { @@ -29919,17 +29202,16 @@ } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/human-signals": { @@ -30211,6 +29493,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/init-package-json": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/init-package-json/-/init-package-json-6.0.3.tgz", @@ -30331,19 +29619,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/inquirer/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -31493,206 +30768,163 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-changed-files/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-circus": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", - "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/expect": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/types": "30.2.0", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", + "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.2.0", - "jest-matcher-utils": "30.2.0", - "jest-message-util": "30.2.0", - "jest-runtime": "30.2.0", - "jest-snapshot": "30.2.0", - "jest-util": "30.2.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "30.2.0", - "pure-rand": "^7.0.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "stack-utils": "^2.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "node_modules/jest-circus/node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "30.2.0" + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "node_modules/jest-circus/node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/jest-circus/node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-circus/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-circus/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "node_modules/jest-circus/node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^3.0.1" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jest-circus/node_modules/chalk": { @@ -31712,10 +30944,26 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-circus/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-circus/node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -31727,118 +30975,258 @@ } } }, - "node_modules/jest-circus/node_modules/jest-diff": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", - "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "node_modules/jest-circus/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@jest/diff-sequences": "30.0.1", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.2.0" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-circus/node_modules/jest-matcher-utils": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", - "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "node_modules/jest-circus/node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jest-circus/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.2.0", - "pretty-format": "30.2.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, "node_modules/jest-circus/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", "slash": "^3.0.0", - "stack-utils": "^2.0.6" + "stack-utils": "^2.0.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "node_modules/jest-circus/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-circus/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/jest-circus/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/jest-circus/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { @@ -31864,6 +31252,20 @@ "node": ">=8" } }, + "node_modules/jest-circus/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/jest-cli": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.2.0.tgz", @@ -31897,45 +31299,6 @@ } } }, - "node_modules/jest-cli/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -31953,37 +31316,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-cli/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-config": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.2.0.tgz", @@ -32036,6 +31368,67 @@ } } }, + "node_modules/jest-config/node_modules/@jest/environment": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", + "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/@jest/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.2.0", + "jest-snapshot": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/@jest/expect-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.2.0.tgz", + "integrity": "sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/@jest/fake-timers": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", + "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.2.0", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/jest-config/node_modules/@jest/schemas": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", @@ -32049,32 +31442,23 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-config/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, + "node_modules/jest-config/node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, "node_modules/jest-config/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -32102,10 +31486,44 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-config/node_modules/dedent": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/expect": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.2.0.tgz", + "integrity": "sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.2.0", + "@jest/get-type": "30.1.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-mock": "30.2.0", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, "node_modules/jest-config/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -32123,32 +31541,177 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-config/node_modules/jest-util": { + "node_modules/jest-config/node_modules/jest-circus": { "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.2.0.tgz", + "integrity": "sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/expect": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.2.0", + "jest-matcher-utils": "30.2.0", + "jest-message-util": "30.2.0", + "jest-runtime": "30.2.0", + "jest-snapshot": "30.2.0", + "jest-util": "30.2.0", + "p-limit": "^3.1.0", + "pretty-format": "30.2.0", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-diff": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.2.0.tgz", + "integrity": "sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-each": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", + "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "@jest/types": "30.2.0", + "chalk": "^4.1.2", + "jest-util": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-environment-node": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", + "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.2.0", + "@jest/fake-timers": "30.2.0", + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-mock": "30.2.0", + "jest-util": "30.2.0", + "jest-validate": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-leak-detector": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", + "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-matcher-utils": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.2.0.tgz", + "integrity": "sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.1.0", + "chalk": "^4.1.2", + "jest-diff": "30.2.0", + "pretty-format": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-mock": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", + "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", "dependencies": { + "@jest/types": "30.2.0", + "@types/node": "*", + "jest-util": "30.2.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-config/node_modules/jest-runner": { + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", + "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.2.0", + "@jest/environment": "30.2.0", + "@jest/test-result": "30.2.0", + "@jest/transform": "30.2.0", "@jest/types": "30.2.0", "@types/node": "*", "chalk": "^4.1.2", - "ci-info": "^4.2.0", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "jest-docblock": "30.2.0", + "jest-environment-node": "30.2.0", + "jest-haste-map": "30.2.0", + "jest-leak-detector": "30.2.0", + "jest-message-util": "30.2.0", + "jest-resolve": "30.2.0", + "jest-runtime": "30.2.0", + "jest-util": "30.2.0", + "jest-watcher": "30.2.0", + "jest-worker": "30.2.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -32157,19 +31720,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-config/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-config/node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -32198,6 +31748,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/jest-config/node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/jest-config/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -32208,6 +31775,27 @@ "node": ">=8" } }, + "node_modules/jest-config/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-config/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", @@ -32283,61 +31871,40 @@ } }, "node_modules/jest-each": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.2.0.tgz", - "integrity": "sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "@jest/types": "30.2.0", - "chalk": "^4.1.2", - "jest-util": "30.2.0", - "pretty-format": "30.2.0" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-each/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -32355,50 +31922,53 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/jest-each/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/jest-each/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-each/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { @@ -32442,10 +32012,28 @@ } } }, + "node_modules/jest-environment-jsdom/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-environment-jsdom/node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { @@ -32455,36 +32043,51 @@ "node": ">=0.4.0" } }, - "node_modules/jest-environment-jsdom/node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "node_modules/jest-environment-jsdom/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "license": "MIT", "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" + "debug": "4" }, "engines": { - "node": ">=0.4.0" + "node": ">= 6.0.0" } }, - "node_modules/jest-environment-jsdom/node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "node_modules/jest-environment-jsdom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-environment-jsdom/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } }, "node_modules/jest-environment-jsdom/node_modules/cssstyle": { "version": "2.3.0", @@ -32521,20 +32124,6 @@ "node": ">=12" } }, - "node_modules/jest-environment-jsdom/node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "license": "MIT", - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/jest-environment-jsdom/node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -32548,6 +32137,53 @@ "node": ">=12" } }, + "node_modules/jest-environment-jsdom/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-environment-jsdom/node_modules/jsdom": { "version": "20.0.3", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", @@ -32660,6 +32296,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", "dev": true, "license": "MIT", "dependencies": { @@ -32704,105 +32341,39 @@ } }, "node_modules/jest-environment-node": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.2.0.tgz", - "integrity": "sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/environment": "30.2.0", - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "30.2.0", - "jest-util": "30.2.0", - "jest-validate": "30.2.0" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-mock": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-environment-node/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node/node_modules/chalk": { @@ -32822,111 +32393,40 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-environment-node/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-environment-node/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/jest-environment-node/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/jest-environment-node/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", @@ -32962,126 +32462,6 @@ "fsevents": "^2.3.3" } }, - "node_modules/jest-haste-map/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map/node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-html-reporter": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/jest-html-reporter/-/jest-html-reporter-4.3.0.tgz", @@ -33105,75 +32485,6 @@ "typescript": "^3.7.x || ^4.3.x || ^5.x" } }, - "node_modules/jest-html-reporter/node_modules/@jest/schemas": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.1.tgz", - "integrity": "sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-html-reporter/node_modules/@jest/types": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.1.tgz", - "integrity": "sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.1", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-html-reporter/node_modules/@sinclair/typebox": { - "version": "0.34.37", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.37.tgz", - "integrity": "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-html-reporter/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-html-reporter/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-junit": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-16.0.0.tgz", @@ -33190,19 +32501,6 @@ "node": ">=10.12.0" } }, - "node_modules/jest-junit/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-junit/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -33214,39 +32512,19 @@ } }, "node_modules/jest-leak-detector": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.2.0.tgz", - "integrity": "sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/get-type": "30.1.0", - "pretty-format": "30.2.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-leak-detector/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-leak-detector/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", @@ -33261,18 +32539,18 @@ } }, "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { @@ -33337,26 +32615,46 @@ } }, "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", + "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.2.0", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.2.0", "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "stack-utils": "^2.0.6" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/jest-message-util/node_modules/@jest/schemas": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", + "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util/node_modules/@sinclair/typebox": { + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", + "dev": true, + "license": "MIT" + }, "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -33375,18 +32673,18 @@ } }, "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", + "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { @@ -33427,14 +32725,73 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-mock-console": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jest-mock-console/-/jest-mock-console-2.0.0.tgz", - "integrity": "sha512-7zrKtXVut+6doalosFxw/2O9spLepQJ9VukODtyLIub2fFkWKe1TyQrxr/GyQogTQcdkHfhvFJdx1OEzLqf/mw==", + "node_modules/jest-mock/node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", - "peerDependencies": { - "jest": ">= 22.4.2" + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-mock/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock/node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-pnp-resolver": { @@ -33587,45 +32944,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-resolve/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-resolve/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -33643,37 +32961,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-resolve/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -33685,120 +32972,163 @@ } }, "node_modules/jest-runner": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.2.0.tgz", - "integrity": "sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/console": "30.2.0", - "@jest/environment": "30.2.0", - "@jest/test-result": "30.2.0", - "@jest/transform": "30.2.0", - "@jest/types": "30.2.0", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", + "chalk": "^4.0.0", "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.2.0", - "jest-environment-node": "30.2.0", - "jest-haste-map": "30.2.0", - "jest-leak-detector": "30.2.0", - "jest-message-util": "30.2.0", - "jest-resolve": "30.2.0", - "jest-runtime": "30.2.0", - "jest-util": "30.2.0", - "jest-watcher": "30.2.0", - "jest-worker": "30.2.0", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/environment": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.2.0.tgz", - "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", + "node_modules/jest-runner/node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/fake-timers": "30.2.0", - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "30.2.0" + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/fake-timers": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.2.0.tgz", - "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", + "node_modules/jest-runner/node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.2.0", - "jest-mock": "30.2.0", - "jest-util": "30.2.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", + "node_modules/jest-runner/node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "license": "MIT", "dependencies": { - "@sinclair/typebox": "^0.34.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-runner/node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "node_modules/jest-runner/node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^3.0.1" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, "node_modules/jest-runner/node_modules/chalk": { @@ -33818,75 +33148,279 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", + "node_modules/jest-runner/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "node_modules/jest-runner/node_modules/jest-mock": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", - "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", + "node_modules/jest-runner/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "jest-util": "30.2.0" + "detect-newline": "^3.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-runner/node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/jest-runner/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "30.2.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner/node_modules/jest-worker": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", - "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.2.0", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" + "supports-color": "^8.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner/node_modules/jest-worker/node_modules/supports-color": { @@ -33905,32 +33439,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest-runner/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-runner/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner/node_modules/pretty-format/node_modules/ansi-styles": { @@ -33946,6 +33467,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/jest-runner/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/jest-runner/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -33977,6 +33508,20 @@ "source-map": "^0.6.0" } }, + "node_modules/jest-runner/node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/jest-runtime": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.2.0.tgz", @@ -34045,45 +33590,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-runtime/node_modules/@sinonjs/fake-timers": { "version": "13.0.5", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", @@ -34122,9 +33628,9 @@ } }, "node_modules/jest-runtime/node_modules/cjs-module-lexer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", - "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true, "license": "MIT" }, @@ -34132,6 +33638,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -34149,27 +33656,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-runtime/node_modules/jest-mock": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", @@ -34185,32 +33671,14 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -34219,47 +33687,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jest-runtime/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-runtime/node_modules/pretty-format": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", - "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-runtime/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -34339,29 +33766,10 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-snapshot/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -34432,27 +33840,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-message-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.2.0.tgz", - "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.2.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.2.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-snapshot/node_modules/jest-mock": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.2.0.tgz", @@ -34468,37 +33855,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-snapshot/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-snapshot/node_modules/pretty-format": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.2.0.tgz", @@ -34527,32 +33883,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", + "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", + "@jest/types": "30.2.0", "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-util/node_modules/chalk": { @@ -34572,20 +33918,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-util/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/jest-validate": { @@ -34619,29 +33962,10 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, "node_modules/jest-validate/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -34710,45 +34034,6 @@ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/jest-watcher/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, "node_modules/jest-watcher/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -34766,37 +34051,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.2.0.tgz", - "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.2.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-watcher/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/jest-websocket-mock": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/jest-websocket-mock/-/jest-websocket-mock-2.5.0.tgz", @@ -34809,19 +34063,20 @@ } }, "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "version": "30.2.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.2.0.tgz", + "integrity": "sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*", - "jest-util": "^29.7.0", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.2.0", "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "supports-color": "^8.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { @@ -34840,62 +34095,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest/node_modules/@jest/types": { - "version": "30.2.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.2.0.tgz", - "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest/node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/joi": { "version": "18.0.2", "resolved": "https://registry.npmjs.org/joi/-/joi-18.0.2.tgz", @@ -35081,16 +34280,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/jsdom/node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -35104,34 +34293,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/jsdom/node_modules/parse5": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", @@ -36062,13 +35223,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lerna/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true, - "license": "ISC" - }, "node_modules/lerna/node_modules/inquirer": { "version": "8.2.7", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.7.tgz", @@ -36302,19 +35456,6 @@ "node": ">=8" } }, - "node_modules/lerna/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lerna/node_modules/tinyglobby": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", @@ -39063,9 +38204,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", "dev": true, "license": "MIT" }, @@ -41830,9 +40971,9 @@ "license": "MIT" }, "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -42712,12 +41853,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -45028,19 +44163,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/renderkid/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/replace-ext": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", @@ -46262,16 +45384,6 @@ "node": ">= 14" } }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, "node_modules/sort-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", @@ -47129,37 +46241,12 @@ "dev": true, "license": "MIT" }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/string.prototype.includes": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", @@ -47271,12 +46358,12 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -47582,9 +46669,9 @@ "license": "MIT" }, "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -48308,9 +47395,9 @@ } }, "node_modules/tough-cookie": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", - "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "peer": true, @@ -48572,18 +47659,6 @@ "node": ">=0.4.0" } }, - "node_modules/ts-node/node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ts-node/node_modules/diff": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", @@ -48593,6 +47668,27 @@ "node": ">=0.3.1" } }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "dev": true, + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", @@ -50468,6 +49564,21 @@ } } }, + "node_modules/vite-tsconfig-paths": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.1.tgz", + "integrity": "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + } + }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.27.3", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", @@ -51482,19 +50593,6 @@ "node": ">=0.4.0" } }, - "node_modules/webpack-bundle-analyzer/node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/webpack-bundle-analyzer/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -51505,28 +50603,6 @@ "node": ">= 10" } }, - "node_modules/webpack-bundle-analyzer/node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-cli": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", @@ -51697,16 +50773,6 @@ "dev": true, "license": "MIT" }, - "node_modules/webpack-dev-server/node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/webpack-dev-server/node_modules/define-lazy-prop": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", @@ -51833,28 +50899,6 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.18.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", - "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/webpack-hot-middleware": { "version": "2.26.1", "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", @@ -52463,9 +51507,9 @@ } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "dev": true, "license": "MIT", "engines": { @@ -53206,7 +52250,6 @@ "devDependencies": { "cross-env": "^10.1.0", "fs-extra": "^11.3.3", - "jest": "^30.2.0", "yeoman-test": "^11.2.0" }, "engines": { @@ -53370,7 +52413,6 @@ "@types/rison": "0.1.0", "@types/seedrandom": "^3.0.8", "fetch-mock": "^12.6.0", - "jest-mock-console": "^2.0.0", "resize-observer-polyfill": "1.5.1", "timezone-mock": "1.4.0" }, @@ -54961,9 +54003,7 @@ "just-handlebars-helpers": "^1.0.19" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/lodash": "^4.17.23", - "jest": "^30.2.0" + "@types/lodash": "^4.17.23" }, "peerDependencies": { "@apache-superset/core": "*", @@ -55002,9 +54042,7 @@ "version": "0.20.3", "license": "Apache-2.0", "devDependencies": { - "@babel/types": "^7.29.0", - "@types/jest": "^30.0.0", - "jest": "^30.2.0" + "@babel/types": "^7.29.0" }, "peerDependencies": { "@ant-design/icons": "^5.2.6", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 543feb1f053..5764bfaa5c1 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -279,7 +279,6 @@ "@testing-library/user-event": "^12.8.3", "@types/content-disposition": "^0.5.9", "@types/dom-to-image": "^2.6.7", - "@types/jest": "^30.0.0", "@types/js-levenshtein": "^1.1.3", "@types/json-bigint": "^1.0.4", "@types/mousetrap": "^1.6.15", @@ -369,6 +368,7 @@ "tsx": "^4.21.0", "typescript": "5.4.5", "unzipper": "^0.12.3", + "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.0.18", "vm-browserify": "^1.1.2", "wait-on": "^9.0.4", diff --git a/superset-frontend/packages/generator-superset/package.json b/superset-frontend/packages/generator-superset/package.json index 2fba902989f..7646e51fa1b 100644 --- a/superset-frontend/packages/generator-superset/package.json +++ b/superset-frontend/packages/generator-superset/package.json @@ -36,7 +36,6 @@ "devDependencies": { "cross-env": "^10.1.0", "fs-extra": "^11.3.3", - "jest": "^30.2.0", "yeoman-test": "^11.2.0" }, "engines": { diff --git a/superset-frontend/packages/generator-superset/test/plugin-chart.test.ts b/superset-frontend/packages/generator-superset/test/plugin-chart.test.ts index 4687d27db5f..4c33f2c3336 100644 --- a/superset-frontend/packages/generator-superset/test/plugin-chart.test.ts +++ b/superset-frontend/packages/generator-superset/test/plugin-chart.test.ts @@ -47,7 +47,7 @@ test('generator-superset:plugin-chart:creates files', async () => { result.assertFile([ '.gitignore', 'babel.config.js', - 'jest.config.js', + 'vi.config.js', 'package.json', 'README.md', 'src/plugin/buildQuery.ts', diff --git a/superset-frontend/packages/superset-core/src/spec/utils/logging.test.ts b/superset-frontend/packages/superset-core/src/spec/utils/logging.test.ts index 11962db4ef9..6b0589156b6 100644 --- a/superset-frontend/packages/superset-core/src/spec/utils/logging.test.ts +++ b/superset-frontend/packages/superset-core/src/spec/utils/logging.test.ts @@ -17,33 +17,33 @@ * under the License. */ beforeEach(() => { - jest.resetModules(); - jest.resetAllMocks(); + vi.resetModules(); + vi.resetAllMocks(); }); test('should pipe to `console` methods', () => { const { logging } = require('@apache-superset/core'); - jest.spyOn(logging, 'debug').mockImplementation(); - jest.spyOn(logging, 'log').mockImplementation(); - jest.spyOn(logging, 'info').mockImplementation(); + vi.spyOn(logging, 'debug').mockImplementation(); + vi.spyOn(logging, 'log').mockImplementation(); + vi.spyOn(logging, 'info').mockImplementation(); expect(() => { logging.debug(); logging.log(); logging.info(); }).not.toThrow(); - jest.spyOn(logging, 'warn').mockImplementation(() => { + vi.spyOn(logging, 'warn').mockImplementation(() => { throw new Error('warn'); }); expect(() => logging.warn()).toThrow('warn'); - jest.spyOn(logging, 'error').mockImplementation(() => { + vi.spyOn(logging, 'error').mockImplementation(() => { throw new Error('error'); }); expect(() => logging.error()).toThrow('error'); - jest.spyOn(logging, 'trace').mockImplementation(() => { + vi.spyOn(logging, 'trace').mockImplementation(() => { throw new Error('Trace:'); }); expect(() => logging.trace()).toThrow('Trace:'); diff --git a/superset-frontend/packages/superset-core/src/ui/theme/utils/themeUtils.test.ts b/superset-frontend/packages/superset-core/src/ui/theme/utils/themeUtils.test.ts index 76b8922668f..9a6b20000f4 100644 --- a/superset-frontend/packages/superset-core/src/ui/theme/utils/themeUtils.test.ts +++ b/superset-frontend/packages/superset-core/src/ui/theme/utils/themeUtils.test.ts @@ -27,16 +27,16 @@ import { Theme } from '../Theme'; import { ThemeAlgorithm } from '../types'; // Mock emotion's cache to avoid actual DOM operations -jest.mock('@emotion/cache', () => ({ +vi.mock('@emotion/cache', () => ({ __esModule: true, - default: jest.fn().mockReturnValue({}), + default: vi.fn().mockReturnValue({}), })); let lightTheme: Theme; let darkTheme: Theme; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Create actual theme instances for testing lightTheme = Theme.fromConfig({ diff --git a/superset-frontend/packages/superset-ui-chart-controls/test/utils/displayTimeRelatedControls.test.ts b/superset-frontend/packages/superset-ui-chart-controls/test/utils/displayTimeRelatedControls.test.ts index 349803a3af5..fc11c28b34b 100644 --- a/superset-frontend/packages/superset-ui-chart-controls/test/utils/displayTimeRelatedControls.test.ts +++ b/superset-frontend/packages/superset-ui-chart-controls/test/utils/displayTimeRelatedControls.test.ts @@ -21,7 +21,7 @@ import { displayTimeRelatedControls } from '../../src'; const mockData = { actions: { - setDatasource: jest.fn(), + setDatasource: vi.fn(), }, controls: { x_axis: { diff --git a/superset-frontend/packages/superset-ui-core/package.json b/superset-frontend/packages/superset-ui-core/package.json index e73b0ec3af2..ce8092f333f 100644 --- a/superset-frontend/packages/superset-ui-core/package.json +++ b/superset-frontend/packages/superset-ui-core/package.json @@ -83,7 +83,6 @@ "@types/rison": "0.1.0", "@types/seedrandom": "^3.0.8", "fetch-mock": "^12.6.0", - "jest-mock-console": "^2.0.0", "resize-observer-polyfill": "1.5.1", "timezone-mock": "1.4.0" }, diff --git a/superset-frontend/packages/superset-ui-core/src/components/ThemedAgGridReact/setupAGGridModules.test.ts b/superset-frontend/packages/superset-ui-core/src/components/ThemedAgGridReact/setupAGGridModules.test.ts index b554073ca2a..57a3f2a7d46 100644 --- a/superset-frontend/packages/superset-ui-core/src/components/ThemedAgGridReact/setupAGGridModules.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/components/ThemedAgGridReact/setupAGGridModules.test.ts @@ -19,9 +19,9 @@ import { ModuleRegistry } from 'ag-grid-community'; import { setupAGGridModules, defaultModules } from './setupAGGridModules'; -jest.mock('ag-grid-community', () => ({ +vi.mock('ag-grid-community', () => ({ ModuleRegistry: { - registerModules: jest.fn(), + registerModules: vi.fn(), }, ColumnAutoSizeModule: { moduleName: 'ColumnAutoSizeModule', @@ -52,7 +52,7 @@ jest.mock('ag-grid-community', () => ({ })); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('defaultModules exports an array of AG Grid modules', () => { @@ -88,7 +88,7 @@ test('setupAGGridModules registers default + additional modules when provided', expect(ModuleRegistry.registerModules).toHaveBeenCalledTimes(1); - const registeredModules = (ModuleRegistry.registerModules as jest.Mock).mock + const registeredModules = (ModuleRegistry.registerModules as vi.Mock).mock .calls[0][0]; // Should contain all default modules diff --git a/superset-frontend/packages/superset-ui-core/src/hooks/useChangeEffect/useChangeEffect.test.ts b/superset-frontend/packages/superset-ui-core/src/hooks/useChangeEffect/useChangeEffect.test.ts index 1c1a96125b9..9b84e157270 100644 --- a/superset-frontend/packages/superset-ui-core/src/hooks/useChangeEffect/useChangeEffect.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/hooks/useChangeEffect/useChangeEffect.test.ts @@ -20,7 +20,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useChangeEffect } from './useChangeEffect'; test('call callback the first time with undefined and value', () => { - const callback = jest.fn(); + const callback = vi.fn(); renderHook(props => useChangeEffect(props.value, props.callback), { initialProps: { value: 'value', callback }, }); @@ -29,7 +29,7 @@ test('call callback the first time with undefined and value', () => { }); test('do not call callback 2 times if the value do not change', () => { - const callback = jest.fn(); + const callback = vi.fn(); const hook = renderHook( props => useChangeEffect(props.value, props.callback), { @@ -41,7 +41,7 @@ test('do not call callback 2 times if the value do not change', () => { }); test('call callback whenever the value changes', () => { - const callback = jest.fn(); + const callback = vi.fn(); const hook = renderHook( props => useChangeEffect(props.value, props.callback), { diff --git a/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidMount/useComponentDidMount.test.ts b/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidMount/useComponentDidMount.test.ts index 7edf9e72329..8ac44a9e11f 100644 --- a/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidMount/useComponentDidMount.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidMount/useComponentDidMount.test.ts @@ -20,7 +20,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useComponentDidMount } from './useComponentDidMount'; test('the effect should only be executed on the first render', () => { - const effect = jest.fn(); + const effect = vi.fn(); const hook = renderHook(() => useComponentDidMount(effect)); expect(effect).toHaveBeenCalledTimes(1); hook.rerender(); diff --git a/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidUpdate/useComponentDidUpdate.test.ts b/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidUpdate/useComponentDidUpdate.test.ts index 1c191b07bea..e1c259d67b3 100644 --- a/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidUpdate/useComponentDidUpdate.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/hooks/useComponentDidUpdate/useComponentDidUpdate.test.ts @@ -20,12 +20,12 @@ import { renderHook } from '@testing-library/react-hooks'; import { useComponentDidUpdate } from './useComponentDidUpdate'; test('the effect should not be executed on the first render', () => { - const effect = jest.fn(); + const effect = vi.fn(); const hook = renderHook(props => useComponentDidUpdate(props.effect), { initialProps: { effect }, }); expect(effect).toHaveBeenCalledTimes(0); - const changedEffect = jest.fn(); + const changedEffect = vi.fn(); hook.rerender({ effect: changedEffect }); expect(changedEffect).toHaveBeenCalledTimes(1); }); diff --git a/superset-frontend/packages/superset-ui-core/src/hooks/useElementOnScreen/useElementOnScreen.test.ts b/superset-frontend/packages/superset-ui-core/src/hooks/useElementOnScreen/useElementOnScreen.test.ts index 9a8321d4edb..e199f4d2617 100644 --- a/superset-frontend/packages/superset-ui-core/src/hooks/useElementOnScreen/useElementOnScreen.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/hooks/useElementOnScreen/useElementOnScreen.test.ts @@ -19,9 +19,11 @@ import { renderHook } from '@testing-library/react-hooks'; import { useElementOnScreen } from './useElementOnScreen'; -const observeMock = jest.fn(); -const unobserveMock = jest.fn(); -const IntersectionObserverMock = jest.fn(); +vi.mock('react', { spy: true }); + +const observeMock = vi.fn(); +const unobserveMock = vi.fn(); +const IntersectionObserverMock = vi.fn(); IntersectionObserverMock.prototype.observe = observeMock; IntersectionObserverMock.prototype.unobserve = unobserveMock; @@ -31,7 +33,7 @@ beforeEach(() => { afterEach(() => { IntersectionObserverMock.mockClear(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('should return null and false on first render', () => { @@ -66,7 +68,7 @@ test('should return isSticky as false when intersectionRatio >= 1', async () => }); test('should observe and unobserve element with IntersectionObserver', async () => { - jest.spyOn(global.React, 'useRef').mockReturnValue({ current: 'test' }); + vi.spyOn(global.React, 'useRef').mockReturnValue({ current: 'test' }); const options = { threshold: 0.5 }; const { result, unmount } = renderHook(() => useElementOnScreen(options)); const [elementRef] = result.current; @@ -85,7 +87,7 @@ test('should observe and unobserve element with IntersectionObserver', async () }); test('should not observe an element if it is null', () => { - jest.spyOn(global.React, 'useRef').mockReturnValue({ current: null }); + vi.spyOn(global.React, 'useRef').mockReturnValue({ current: null }); const options = {}; const { result } = renderHook(() => useElementOnScreen(options)); const [ref, isSticky] = result.current; @@ -96,7 +98,7 @@ test('should not observe an element if it is null', () => { }); test('should not unobserve the element if it is null', () => { - jest.spyOn(global.React, 'useRef').mockReturnValue({ current: null }); + vi.spyOn(global.React, 'useRef').mockReturnValue({ current: null }); const options = {}; const { result, unmount } = renderHook(() => useElementOnScreen(options)); const [ref, isSticky] = result.current; diff --git a/superset-frontend/packages/superset-ui-core/src/hooks/useTruncation/useChildElementTruncation.test.ts b/superset-frontend/packages/superset-ui-core/src/hooks/useTruncation/useChildElementTruncation.test.ts index 7441c259874..dd387e63dca 100644 --- a/superset-frontend/packages/superset-ui-core/src/hooks/useTruncation/useChildElementTruncation.test.ts +++ b/superset-frontend/packages/superset-ui-core/src/hooks/useTruncation/useChildElementTruncation.test.ts @@ -19,9 +19,10 @@ import { renderHook } from '@testing-library/react-hooks'; import { RefObject } from 'react'; import useChildElementTruncation from './useChildElementTruncation'; +import { Mock } from 'vitest'; -let observeMock: jest.Mock; -let disconnectMock: jest.Mock; +let observeMock: Mock; +let disconnectMock: Mock; let originalResizeObserver: typeof ResizeObserver; const genElements = ( @@ -79,12 +80,14 @@ beforeAll(() => { originalResizeObserver = window.ResizeObserver; // Mock ResizeObserver - observeMock = jest.fn(); - disconnectMock = jest.fn(); - window.ResizeObserver = jest.fn(() => ({ - observe: observeMock, - disconnect: disconnectMock, - })) as unknown as typeof ResizeObserver; + observeMock = vi.fn(); + disconnectMock = vi.fn(); + window.ResizeObserver = vi.fn(function () { + return { + observe: observeMock, + disconnect: disconnectMock, + }; + } as unknown as typeof window.ResizeObserver); }); afterAll(() => { diff --git a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts index 28dfb93abe3..02eb6880f90 100644 --- a/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/color/CategoricalColorScale.test.ts @@ -64,11 +64,11 @@ describe('CategoricalColorScale', () => { describe('.getColor(value, sliceId)', () => { let scale: CategoricalColorScale; - let addSliceSpy: jest.SpyInstance< + let addSliceSpy: vi.SpyInstance< void, [label: string, color: string, sliceId: number, colorScheme?: string] >; - let getNextAvailableColorSpy: jest.SpyInstance< + let getNextAvailableColorSpy: vi.SpyInstance< string, [currentLabel: string, currentColor: string] >; @@ -76,14 +76,14 @@ describe('CategoricalColorScale', () => { beforeEach(() => { scale = new CategoricalColorScale(['blue', 'red', 'green']); // Spy on the addSlice method of labelsColorMapInstance - addSliceSpy = jest.spyOn(scale.labelsColorMapInstance, 'addSlice'); + addSliceSpy = vi.spyOn(scale.labelsColorMapInstance, 'addSlice'); getNextAvailableColorSpy = jest .spyOn(scale, 'getNextAvailableColor') .mockImplementation(color => color); }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); test('uses labelsColorMapInstance color map when source is Dashboard, otherwise uses chartLabelsColorMap', () => { @@ -406,7 +406,7 @@ describe('CategoricalColorScale', () => { const scale = new CategoricalColorScale(['blue', 'red', 'green']); // Mock or override getColorUsageCount to return 0 for "blue" - jest.spyOn(scale, 'getColorUsageCount').mockImplementation(color => { + vi.spyOn(scale, 'getColorUsageCount').mockImplementation(color => { if (color === 'blue') return 0; // Explicitly return 0 for "blue" return 1; // Return 1 for other colors }); diff --git a/superset-frontend/packages/superset-ui-core/test/color/LabelsColorMapSingleton.test.ts b/superset-frontend/packages/superset-ui-core/test/color/LabelsColorMapSingleton.test.ts index 53ecc6ffadb..cef5bbef06c 100644 --- a/superset-frontend/packages/superset-ui-core/test/color/LabelsColorMapSingleton.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/color/LabelsColorMapSingleton.test.ts @@ -27,9 +27,10 @@ import { LabelsColorMap, } from '@superset-ui/core'; -const actual = jest.requireActual('../../src/color/utils'); -const getAnalogousColorsSpy = jest - .spyOn(actual, 'getAnalogousColors') +import * as colorUtils from '../../src/color/utils'; + +const getAnalogousColorsSpy = vi + .spyOn(colorUtils, 'getAnalogousColors') .mockImplementation(() => ['red', 'green', 'blue']); describe('LabelsColorMap', () => { @@ -141,8 +142,8 @@ describe('LabelsColorMap', () => { labelsColorMap = getLabelsColorMap(); categoricalNamespace = CategoricalColorNamespace.getNamespace(undefined); mockedNamespace = { - getScale: jest.fn().mockReturnValue({ - getColor: jest.fn(() => 'mockColor'), + getScale: vi.fn().mockReturnValue({ + getColor: vi.fn(() => 'mockColor'), }), }; }); diff --git a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts index 92efac60892..9607dc953f2 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClient.test.ts @@ -73,18 +73,18 @@ describe('SupersetClient', () => { fetchMock.put(mockPutUrl, mockPutPayload); fetchMock.get(mockRequestUrl, mockGetPayload); - const initSpy = jest.spyOn(SupersetClientClass.prototype, 'init'); - const getSpy = jest.spyOn(SupersetClientClass.prototype, 'get'); - const postSpy = jest.spyOn(SupersetClientClass.prototype, 'post'); - const putSpy = jest.spyOn(SupersetClientClass.prototype, 'put'); - const deleteSpy = jest.spyOn(SupersetClientClass.prototype, 'delete'); - const authenticatedSpy = jest.spyOn( + const initSpy = vi.spyOn(SupersetClientClass.prototype, 'init'); + const getSpy = vi.spyOn(SupersetClientClass.prototype, 'get'); + const postSpy = vi.spyOn(SupersetClientClass.prototype, 'post'); + const putSpy = vi.spyOn(SupersetClientClass.prototype, 'put'); + const deleteSpy = vi.spyOn(SupersetClientClass.prototype, 'delete'); + const authenticatedSpy = vi.spyOn( SupersetClientClass.prototype, 'isAuthenticated', ); - const csrfSpy = jest.spyOn(SupersetClientClass.prototype, 'fetchCSRFToken'); - const requestSpy = jest.spyOn(SupersetClientClass.prototype, 'request'); - const getGuestTokenSpy = jest.spyOn( + const csrfSpy = vi.spyOn(SupersetClientClass.prototype, 'fetchCSRFToken'); + const requestSpy = vi.spyOn(SupersetClientClass.prototype, 'request'); + const getGuestTokenSpy = vi.spyOn( SupersetClientClass.prototype, 'getGuestToken', ); diff --git a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts index e5020b20812..00f457bff74 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/SupersetClientClass.test.ts @@ -296,7 +296,7 @@ describe('SupersetClientClass', () => { test('checks for authentication before every get and post request', async () => { expect.assertions(6); - const authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth'); + const authSpy = vi.spyOn(SupersetClientClass.prototype, 'ensureAuth'); const client = new SupersetClientClass({ protocol, host }); await client.init(); @@ -521,7 +521,7 @@ describe('SupersetClientClass', () => { describe('when unauthorized', () => { let originalLocation: any; - let authSpy: jest.SpyInstance; + let authSpy: vi.SpyInstance; const mockRequestUrl = 'https://host/get/url'; const mockRequestPath = '/get/url'; const mockRequestSearch = '?param=1¶m=2'; @@ -602,7 +602,7 @@ describe('SupersetClientClass', () => { }); test('accepts an unauthorizedHandler to override redirect behavior', async () => { - const unauthorizedHandler = jest.fn(); + const unauthorizedHandler = vi.fn(); const client = new SupersetClientClass({ unauthorizedHandler }); let error; @@ -630,7 +630,7 @@ describe('SupersetClientClass', () => { const guestToken = 'test-guest-token'; const postFormPayload = { number: 123, array: [1, 2, 3] }; - let authSpy: jest.SpyInstance; + let authSpy: vi.SpyInstance; let client: SupersetClientClass; let appendChild: any; let removeChild: any; @@ -642,13 +642,13 @@ describe('SupersetClientClass', () => { fetchMock.get(LOGIN_GLOB, { result: 1234 }, { name: LOGIN_GLOB }); client = new SupersetClientClass({ protocol, host }); - authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth'); + authSpy = vi.spyOn(SupersetClientClass.prototype, 'ensureAuth'); await client.init(); - appendChild = jest.fn(); - removeChild = jest.fn(); - submit = jest.fn(); - createElement = jest.fn(() => ({ - appendChild: jest.fn(), + appendChild = vi.fn(); + removeChild = vi.fn(); + submit = vi.fn(); + createElement = vi.fn(() => ({ + appendChild: vi.fn(), submit, })); @@ -658,7 +658,7 @@ describe('SupersetClientClass', () => { }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); test.each(['', '/prefix'])( @@ -666,7 +666,7 @@ describe('SupersetClientClass', () => { async appRoot => { if (appRoot !== '') { client = new SupersetClientClass({ protocol, host, appRoot }); - authSpy = jest.spyOn(SupersetClientClass.prototype, 'ensureAuth'); + authSpy = vi.spyOn(SupersetClientClass.prototype, 'ensureAuth'); await client.init(); } await client.postForm(mockPostFormEndpoint, {}); diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts index 8d45af31a53..98a140776be 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApi.test.ts @@ -207,7 +207,7 @@ describe('callApi()', () => { // corruptObject has no toString method and will fail cast to String() corrupt: [corruptObject], }; - jest.spyOn(console, 'error').mockImplementation(); + vi.spyOn(console, 'error').mockImplementation(); await callApi({ url: mockPostUrl, diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApiAndParseWithTimeout.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApiAndParseWithTimeout.test.ts index f1afcc3017f..89f73cc6194 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApiAndParseWithTimeout.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/callApiAndParseWithTimeout.test.ts @@ -40,12 +40,12 @@ describe('callApiAndParseWithTimeout()', () => { afterEach(() => { fetchMock.removeRoutes().clearHistory(); - jest.useRealTimers(); + vi.useRealTimers(); }); describe('callApi', () => { test('calls callApi()', () => { - const callApiSpy = jest.spyOn(callApi, 'default'); + const callApiSpy = vi.spyOn(callApi, 'default'); callApiAndParseWithTimeout({ url: mockGetUrl, method: 'GET' }); expect(callApiSpy).toHaveBeenCalledTimes(1); @@ -55,7 +55,7 @@ describe('callApiAndParseWithTimeout()', () => { describe('parseResponse', () => { test('calls parseResponse()', async () => { - const parseSpy = jest.spyOn(parseResponse, 'default'); + const parseSpy = vi.spyOn(parseResponse, 'default'); await callApiAndParseWithTimeout({ url: mockGetUrl, @@ -69,7 +69,7 @@ describe('callApiAndParseWithTimeout()', () => { describe('timeout', () => { test('does not create a rejection timer if no timeout passed', () => { - const rejectionSpy = jest.spyOn(rejectAfterTimeout, 'default'); + const rejectionSpy = vi.spyOn(rejectAfterTimeout, 'default'); callApiAndParseWithTimeout({ url: mockGetUrl, method: 'GET' }); expect(rejectionSpy).toHaveBeenCalledTimes(0); @@ -77,8 +77,8 @@ describe('callApiAndParseWithTimeout()', () => { }); test('creates a rejection timer if a timeout passed', () => { - jest.useFakeTimers(); // prevents the timeout from rejecting + failing test - const rejectionSpy = jest.spyOn(rejectAfterTimeout, 'default'); + vi.useFakeTimers(); // prevents the timeout from rejecting + failing test + const rejectionSpy = vi.spyOn(rejectAfterTimeout, 'default'); callApiAndParseWithTimeout({ url: mockGetUrl, method: 'GET', @@ -91,7 +91,7 @@ describe('callApiAndParseWithTimeout()', () => { test('rejects if the request exceeds the timeout', async () => { expect.assertions(2); - jest.useFakeTimers(); + vi.useFakeTimers(); const mockTimeoutUrl = '/mock/timeout/url'; const unresolvingPromise = new Promise(() => {}); @@ -104,7 +104,7 @@ describe('callApiAndParseWithTimeout()', () => { method: 'GET', timeout: 1, }); - jest.advanceTimersByTime(2); + vi.advanceTimersByTime(2); await promise; } catch (err) { error = err; diff --git a/superset-frontend/packages/superset-ui-core/test/connection/callApi/rejectAfterTimeout.test.ts b/superset-frontend/packages/superset-ui-core/test/connection/callApi/rejectAfterTimeout.test.ts index cbd18d40d18..2dda207ce1d 100644 --- a/superset-frontend/packages/superset-ui-core/test/connection/callApi/rejectAfterTimeout.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/connection/callApi/rejectAfterTimeout.test.ts @@ -21,18 +21,18 @@ import rejectAfterTimeout from '../../../src/connection/callApi/rejectAfterTimeo describe('rejectAfterTimeout()', () => { test('returns a promise that rejects after the specified timeout', async () => { expect.assertions(1); - jest.useFakeTimers(); + vi.useFakeTimers(); let error; try { const promise = rejectAfterTimeout(10); - jest.advanceTimersByTime(11); + vi.advanceTimersByTime(11); await promise; } catch (err) { error = err; } finally { expect(error).toBeDefined(); } - jest.useRealTimers(); + vi.useRealTimers(); }); }); diff --git a/superset-frontend/packages/superset-ui-core/test/dimension/svg/LazyFactory.test.ts b/superset-frontend/packages/superset-ui-core/test/dimension/svg/LazyFactory.test.ts index 34dd03faa92..f2ac397d725 100644 --- a/superset-frontend/packages/superset-ui-core/test/dimension/svg/LazyFactory.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/dimension/svg/LazyFactory.test.ts @@ -29,7 +29,7 @@ describe('LazyFactory', () => { expect(innerDiv.parentNode).toEqual(div); }); test('reuses existing', () => { - const factoryFn = jest.fn(() => document.createElement('div')); + const factoryFn = vi.fn(() => document.createElement('div')); const factory = new LazyFactory(factoryFn); const div1 = factory.createInContainer(); const div2 = factory.createInContainer(); diff --git a/superset-frontend/packages/superset-ui-core/test/dynamic-plugins/shared-modules.test.ts b/superset-frontend/packages/superset-ui-core/test/dynamic-plugins/shared-modules.test.ts index 26968985c6e..7ce7a8c311d 100644 --- a/superset-frontend/packages/superset-ui-core/test/dynamic-plugins/shared-modules.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/dynamic-plugins/shared-modules.test.ts @@ -26,7 +26,7 @@ describe('shared modules', () => { test('assigns to window', async () => { const fakeModule = { foo: 'bar' }; - const fetchModule = jest.fn().mockResolvedValue(fakeModule); + const fetchModule = vi.fn().mockResolvedValue(fakeModule); await defineSharedModule('test-module', fetchModule); @@ -37,7 +37,7 @@ describe('shared modules', () => { test('resolves to the same reference every time', async () => { const fakeModule = { foo: 'bar' }; - const fetchModule = jest.fn().mockResolvedValue(fakeModule); + const fetchModule = vi.fn().mockResolvedValue(fakeModule); const result1 = await defineSharedModule('test-module', fetchModule); const result2 = await defineSharedModule('test-module', fetchModule); @@ -48,8 +48,8 @@ describe('shared modules', () => { test('does not redefine unnecessarily', async () => { const fakeModule = { foo: 'bar' }; - const fetchModule = jest.fn().mockResolvedValue(fakeModule); - const duplicateFetchModule = jest.fn().mockResolvedValue(fakeModule); + const fetchModule = vi.fn().mockResolvedValue(fakeModule); + const duplicateFetchModule = vi.fn().mockResolvedValue(fakeModule); const result1 = await defineSharedModule('test-module', fetchModule); const result2 = await defineSharedModule( @@ -65,7 +65,7 @@ describe('shared modules', () => { test('deduplicates in-progress definitions', async () => { const fakeModule = { foo: 'bar' }; // get a promise that actually takes a moment; - const fetchModule = jest + const fetchModule = vi .fn() .mockImplementation(() => Promise.resolve(setImmediate).then(() => fakeModule), diff --git a/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts b/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts index f8e041f07af..ac4139cb9a2 100644 --- a/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/models/Registry.test.ts @@ -387,10 +387,10 @@ describe('Registry', () => { describe('listeners', () => { let registry = new Registry(); - let listener = jest.fn(); + let listener = vi.fn(); beforeEach(() => { registry = new Registry(); - listener = jest.fn(); + listener = vi.fn(); registry.addListener(listener); }); @@ -447,10 +447,10 @@ describe('Registry', () => { }); test('keeps working', () => { - const errorListener = jest.fn().mockImplementation(() => { + const errorListener = vi.fn().mockImplementation(() => { throw new Error('test error'); }); - const lastListener = jest.fn(); + const lastListener = vi.fn(); registry.addListener(errorListener); registry.addListener(lastListener); diff --git a/superset-frontend/packages/superset-ui-core/test/query/api/v1/makeApi.test.ts b/superset-frontend/packages/superset-ui-core/test/query/api/v1/makeApi.test.ts index 519680c0a52..d9b30374d59 100644 --- a/superset-frontend/packages/superset-ui-core/test/query/api/v1/makeApi.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/query/api/v1/makeApi.test.ts @@ -47,7 +47,7 @@ describe('makeApi()', () => { }); const client = new SupersetClientClass({ appRoot: '/foo' }); const mockResponse = { yes: 'ok' }; - const mockRequest = jest.fn(() => + const mockRequest = vi.fn(() => Promise.resolve( new Response(JSON.stringify(mockResponse), { headers: { 'Content-Type': 'application/json' }, diff --git a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts index 600c6d8605e..a6e3ecd3fdd 100644 --- a/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/query/buildQueryContext.test.ts @@ -125,10 +125,7 @@ describe('buildQueryContext', () => { ]); }); test('should call normalizeTimeColumn if has x_axis', () => { - const spyNormalizeTimeColumn = jest.spyOn( - queryModule, - 'normalizeTimeColumn', - ); + const spyNormalizeTimeColumn = vi.spyOn(queryModule, 'normalizeTimeColumn'); buildQueryContext( { diff --git a/superset-frontend/packages/superset-ui-core/test/time-comparison/computeCustomDateTime.test.ts b/superset-frontend/packages/superset-ui-core/test/time-comparison/computeCustomDateTime.test.ts index 457cc3a4531..ca210860188 100644 --- a/superset-frontend/packages/superset-ui-core/test/time-comparison/computeCustomDateTime.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/time-comparison/computeCustomDateTime.test.ts @@ -23,12 +23,12 @@ const TODAY = '2024-06-03'; // Mock Date to always return 2024-06-03 beforeEach(() => { - jest.useFakeTimers(); - jest.setSystemTime(new Date(TODAY).getTime()); + vi.useFakeTimers(); + vi.setSystemTime(new Date(TODAY).getTime()); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); test('should return the current date for "now"', () => { diff --git a/superset-frontend/packages/superset-ui-core/test/time-comparison/getTimeOffset.test.ts b/superset-frontend/packages/superset-ui-core/test/time-comparison/getTimeOffset.test.ts index 1f95559c20d..64220d111fe 100644 --- a/superset-frontend/packages/superset-ui-core/test/time-comparison/getTimeOffset.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/time-comparison/getTimeOffset.test.ts @@ -27,7 +27,7 @@ const NOW_UTC_IN_PACIFIC = '2024-06-03T08:00:00Z'; // Same as 2024-06-03T00:00:0 afterEach(() => { timezoneMock.unregister(); - jest.useRealTimers(); + vi.useRealTimers(); }); const runTimezoneTest = ( @@ -40,7 +40,7 @@ const runTimezoneTest = ( includeFutureOffsets = true, ) => { timezoneMock.register(timezone); - jest.setSystemTime(new Date(now_time)); + vi.setSystemTime(new Date(now_time)); const result = getTimeOffset({ timeRangeFilter, shifts, @@ -52,9 +52,9 @@ const runTimezoneTest = ( }; test('should handle includeFutureOffsets is null', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); timezoneMock.register('Etc/GMT-2'); - jest.setSystemTime(new Date(NOW_UTC_IN_EUROPE)); + vi.setSystemTime(new Date(NOW_UTC_IN_EUROPE)); const result = getTimeOffset({ timeRangeFilter: { comparator: '2024-06-03 : 2024-06-10', @@ -72,7 +72,7 @@ test('should handle custom range with specific dates', () => { }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -101,7 +101,7 @@ test('should handle custom range with relative dates (now)', () => { }; const shifts = ['custom']; const startDate = '2024-05-30'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -130,7 +130,7 @@ test('should handle inherit shift', () => { }; const shifts = ['inherit']; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -158,7 +158,7 @@ test('should handle custom and inherit shifts', () => { }; const shifts = ['custom', 'inherit']; const startDate = '2024-05-28'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -187,7 +187,7 @@ test('should handle no shifts', () => { }; const shifts: any = []; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -211,7 +211,7 @@ test('should handle null timeRangeFilter', () => { const timeRangeFilter = null; const shifts = ['custom']; const startDate = '2024-06-01'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -239,7 +239,7 @@ test('should handle predefined shifts', () => { }; const shifts: any = ['1 year ago']; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -269,7 +269,7 @@ test('should handle custom range with DATEADD function', () => { }; const shifts = ['custom']; const startDate = '2024-05-21'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -297,7 +297,7 @@ test('should handle custom range with DATEADD function and relative start date', }; const shifts = ['custom']; const startDate = '2024-06-01'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -325,7 +325,7 @@ test('should handle custom range with DATEADD function and relative end date', ( }; const shifts = ['custom']; const startDate = '2024-05-23'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -354,7 +354,7 @@ test('should handle custom range with specific date and relative end date', () = }; const shifts = ['custom']; const startDate = '2024-05-23'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -383,7 +383,7 @@ test('should handle custom range with specific date and specific end date', () = }; const shifts = ['custom']; const startDate = '2024-06-01'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -411,7 +411,7 @@ test('should handle custom range with Last and now', () => { }; const shifts = ['custom']; const startDate = '2024-05-30'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -439,7 +439,7 @@ test('should handle custom range with Last week', () => { }; const shifts = ['custom']; const startDate = '2024-05-21'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -467,7 +467,7 @@ test('should handle custom range with previous calendar week', () => { }; const shifts = ['custom']; const startDate = '2024-05-26'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-06-05T02:06:00+02:00', 'Etc/GMT-2', @@ -500,7 +500,7 @@ test('should handle custom range with previous calendar month', () => { }; const shifts = ['custom']; const startDate = '2024-04-26'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-06-05T02:06:00+02:00', 'Etc/GMT-2', @@ -533,7 +533,7 @@ test('should handle custom range with previous calendar year', () => { }; const shifts = ['custom']; const startDate = '2022-12-26'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-06-05T02:06:00+02:00', @@ -567,7 +567,7 @@ test('should handle custom range with Advanced 2022-11-01', () => { }; const shifts = ['custom']; const startDate = '2022-10-18'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-06-05T02:06:00+02:00', @@ -601,7 +601,7 @@ test('should handle future inherit shift with includeFutureOffsets set to true', }; const shifts = ['inherit']; const startDate = '2024-06-20'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -638,7 +638,7 @@ test('should handle future custom shift with includeFutureOffsets set to true', }; const shifts = ['custom']; const startDate = '2024-06-15'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -667,7 +667,7 @@ test('should handle custom range with specific (YYYY-MM) and relative dates', () }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -696,7 +696,7 @@ test('should handle custom range with minutes', () => { }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -725,7 +725,7 @@ test('should handle custom range with undefined startDate', () => { }; const shifts = ['custom']; const startDate = undefined; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -752,7 +752,7 @@ test('should handle future custom shift with different format', () => { }; const shifts = ['custom']; const startDate = '2024-06-15'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -782,7 +782,7 @@ test('should handle custom range with relative dates', () => { }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -812,7 +812,7 @@ test('should handle custom range with relative dates (minute and seconds)', () = }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -842,7 +842,7 @@ test('should handle custom range with relative dates (hour)', () => { }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -871,7 +871,7 @@ test('should handle custom shifts with same day', () => { }; const shifts = ['custom']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -900,7 +900,7 @@ test('should handle inherit shifts without filter', () => { }; const shifts = ['inherit']; const startDate = '2024-05-29'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, @@ -927,7 +927,7 @@ test('should handle inherit shift same day', () => { }; const shifts = ['inherit']; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -963,7 +963,7 @@ test('should handle inherit shift same day includeFutureOffsets set to false', ( }; const shifts = ['inherit']; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', @@ -999,7 +999,7 @@ test('should handle custom shift same day includeFutureOffsets set to false', () }; const shifts = ['custom']; const startDate = '2024-03-06'; - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( NOW_UTC_IN_EUROPE, 'Etc/GMT-2', diff --git a/superset-frontend/packages/superset-ui-core/test/time-comparison/parseDttmToDate.test.ts b/superset-frontend/packages/superset-ui-core/test/time-comparison/parseDttmToDate.test.ts index e4f805e42c8..ea4c766f3ef 100644 --- a/superset-frontend/packages/superset-ui-core/test/time-comparison/parseDttmToDate.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/time-comparison/parseDttmToDate.test.ts @@ -27,7 +27,7 @@ const NOW_UTC_IN_PACIFIC = '2024-06-03T08:00:00Z'; // Same as 2024-06-03T00:00:0 afterEach(() => { timezoneMock.unregister(); - jest.useRealTimers(); + vi.useRealTimers(); }); const runTimezoneTest = ( @@ -39,7 +39,7 @@ const runTimezoneTest = ( computingShift = false, ) => { timezoneMock.register(timezone); - jest.setSystemTime(new Date(now_time)); + vi.setSystemTime(new Date(now_time)); expect(parseDttmToDate(eval_time, endDate, computingShift)).toEqual( expected_result, ); @@ -47,7 +47,7 @@ const runTimezoneTest = ( }; test('should return the current date for "now"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'now', NOW_UTC_IN_EUROPE, @@ -64,7 +64,7 @@ test('should return the current date for "now"', () => { }); test('should return the current date for "today"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'today', NOW_UTC_IN_EUROPE, @@ -81,7 +81,7 @@ test('should return the current date for "today"', () => { }); test('should return the current date for "No filter"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'No filter', NOW_UTC_IN_EUROPE, @@ -103,7 +103,7 @@ test('should return the current date for "No filter"', () => { }); test('should return the current date for an empty string', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '', NOW_UTC_IN_EUROPE, @@ -120,7 +120,7 @@ test('should return the current date for an empty string', () => { }); test('should return yesterday date for "Last day"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last day', NOW_UTC_IN_EUROPE, @@ -142,7 +142,7 @@ test('should return yesterday date for "Last day"', () => { }); test('should return the date one week ago for "Last week"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last week', NOW_UTC_IN_EUROPE, @@ -164,7 +164,7 @@ test('should return the date one week ago for "Last week"', () => { }); test('should return the date one month ago for "Last month"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last month', NOW_UTC_IN_EUROPE, @@ -186,7 +186,7 @@ test('should return the date one month ago for "Last month"', () => { }); test('should return the date three months ago for "Last quarter"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last quarter', NOW_UTC_IN_EUROPE, @@ -208,7 +208,7 @@ test('should return the date three months ago for "Last quarter"', () => { }); test('should return the date one year ago for "Last year"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last year', NOW_UTC_IN_EUROPE, @@ -230,7 +230,7 @@ test('should return the date one year ago for "Last year"', () => { }); test('should return the date for "previous calendar week"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar week', '2024-06-04T22:00:00Z', @@ -252,7 +252,7 @@ test('should return the date for "previous calendar week"', () => { }); test('should return the date for "previous calendar month"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar month', NOW_UTC_IN_EUROPE, @@ -274,7 +274,7 @@ test('should return the date for "previous calendar month"', () => { }); test('should return the date for "previous calendar year"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar year', NOW_UTC_IN_EUROPE, @@ -296,7 +296,7 @@ test('should return the date for "previous calendar year"', () => { }); test('should return the date for "1 day ago"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '1 day ago', NOW_UTC_IN_EUROPE, @@ -318,7 +318,7 @@ test('should return the date for "1 day ago"', () => { }); test('should return the date for "1 week ago"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '1 week ago', NOW_UTC_IN_EUROPE, @@ -340,7 +340,7 @@ test('should return the date for "1 week ago"', () => { }); test('should return the date for "1 month ago"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '1 month ago', NOW_UTC_IN_EUROPE, @@ -362,7 +362,7 @@ test('should return the date for "1 month ago"', () => { }); test('should return the date for "1 year ago"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '1 year ago', NOW_UTC_IN_EUROPE, @@ -384,7 +384,7 @@ test('should return the date for "1 year ago"', () => { }); test('should return the date for "2024-03-09"', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-03-09', NOW_UTC_IN_EUROPE, @@ -406,7 +406,7 @@ test('should return the date for "2024-03-09"', () => { }); test('should return the current date for "Last day" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last day', NOW_UTC_IN_EUROPE, @@ -431,7 +431,7 @@ test('should return the current date for "Last day" with isEndDate true', () => }); test('should return the current date for "Last week" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last week', NOW_UTC_IN_EUROPE, @@ -456,7 +456,7 @@ test('should return the current date for "Last week" with isEndDate true', () => }); test('should return the current date for "Last quarter" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last quarter', NOW_UTC_IN_EUROPE, @@ -481,7 +481,7 @@ test('should return the current date for "Last quarter" with isEndDate true', () }); test('should return the current date for "Last year" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'Last year', NOW_UTC_IN_EUROPE, @@ -506,7 +506,7 @@ test('should return the current date for "Last year" with isEndDate true', () => }); test('should return the date for "previous calendar week" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar week', NOW_UTC_IN_EUROPE, @@ -531,7 +531,7 @@ test('should return the date for "previous calendar week" with isEndDate true', }); test('should return the date for "previous calendar month" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar month', NOW_UTC_IN_EUROPE, @@ -556,7 +556,7 @@ test('should return the date for "previous calendar month" with isEndDate true', }); test('should return the date for "previous calendar year" with isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( 'previous calendar year', NOW_UTC_IN_EUROPE, @@ -581,7 +581,7 @@ test('should return the date for "previous calendar year" with isEndDate true', }); test('should return the date for "2024" with parts.length === 1', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024', NOW_UTC_IN_EUROPE, @@ -598,7 +598,7 @@ test('should return the date for "2024" with parts.length === 1', () => { }); test('should return the date for "2024-03" with parts.length === 2', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-03', NOW_UTC_IN_EUROPE, @@ -620,7 +620,7 @@ test('should return the date for "2024-03" with parts.length === 2', () => { }); test('should return the date for "2024-03-06" with parts.length === 3', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); runTimezoneTest( '2024-03-06', NOW_UTC_IN_EUROPE, @@ -642,7 +642,7 @@ test('should return the date for "2024-03-06" with parts.length === 3', () => { }); test('should return the date for "2024-03-06" with computingShifts true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const expectedDate = new Date('2024-03-05T22:00:00Z'); expectedDate.setHours(-expectedDate.getTimezoneOffset() / 60, 0, 0, 0); runTimezoneTest( @@ -656,7 +656,7 @@ test('should return the date for "2024-03-06" with computingShifts true', () => }); test('should return the date for "2024-03-06" with computingShifts true and isEndDate true', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const expectedDate = new Date('2024-03-05T22:00:00Z'); expectedDate.setHours(-expectedDate.getTimezoneOffset() / 60, 0, 0, 0); runTimezoneTest( diff --git a/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts index 0bdfae3ddc3..c117d992fe3 100644 --- a/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/utils/featureFlag.test.ts @@ -52,7 +52,7 @@ test('does nothing if feature flags are already initialized', () => { }); test('returns false and raises console error if feature flags have not been initialized', () => { - const logging = jest.spyOn(uiCore.logging, 'error'); + const logging = vi.spyOn(uiCore.logging, 'error'); Object.defineProperty(window, 'featureFlags', { value: undefined, }); diff --git a/superset-frontend/packages/superset-ui-core/test/utils/getSelectedText.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/getSelectedText.test.ts index 0f827ff17f5..813b1d463c8 100644 --- a/superset-frontend/packages/superset-ui-core/test/utils/getSelectedText.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/utils/getSelectedText.test.ts @@ -19,9 +19,9 @@ import { getSelectedText } from '@superset-ui/core'; test('Returns null if Selection object is null', () => { - jest.spyOn(window, 'getSelection').mockImplementationOnce(() => null); + vi.spyOn(window, 'getSelection').mockImplementationOnce(() => null); expect(getSelectedText()).toEqual(undefined); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); test('Returns selection text if Selection object is not null', () => { @@ -29,5 +29,5 @@ test('Returns selection text if Selection object is not null', () => { .spyOn(window, 'getSelection') .mockImplementationOnce(() => ({ toString: () => 'test string' }) as any); expect(getSelectedText()).toEqual('test string'); - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); diff --git a/superset-frontend/packages/superset-ui-core/test/utils/promiseTimeout.test.ts b/superset-frontend/packages/superset-ui-core/test/utils/promiseTimeout.test.ts index 1a1ed0fe5b4..bbf36b68101 100644 --- a/superset-frontend/packages/superset-ui-core/test/utils/promiseTimeout.test.ts +++ b/superset-frontend/packages/superset-ui-core/test/utils/promiseTimeout.test.ts @@ -21,15 +21,15 @@ import { promiseTimeout } from '@superset-ui/core'; describe('promiseTimeout(func, delay)', () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); test('resolves after delay', async () => { const promise = promiseTimeout(() => 'abcd', 10); - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); const result = await promise; expect(result).toEqual('abcd'); expect(result).toHaveLength(4); @@ -40,7 +40,7 @@ describe('promiseTimeout(func, delay)', () => { promiseTimeout(() => 'abc', 10), promiseTimeout(() => 'def', 20), ]); - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); const result = await promise; expect(result).toEqual('abc'); }); diff --git a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts index 5bfe77903ff..dcd5c6b46a0 100644 --- a/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts +++ b/superset-frontend/packages/superset-ui-switchboard/src/switchboard.test.ts @@ -113,8 +113,8 @@ describe('comms', () => { }); beforeEach(() => { - console.debug = jest.fn(); // silencio bruno - console.error = jest.fn(); + console.debug = vi.fn(); // silencio bruno + console.error = vi.fn(); }); afterEach(() => { @@ -158,7 +158,7 @@ describe('comms', () => { const channel = new MessageChannel(); const ours = new Switchboard({ port: channel.port1, name: 'ours' }); const theirs = new Switchboard({ port: channel.port2, name: 'theirs' }); - const handler = jest.fn(); + const handler = vi.fn(); theirs.defineMethod('someEvent', handler); theirs.start(); @@ -173,7 +173,7 @@ describe('comms', () => { const ours = new Switchboard({ port: channel.port1, name: 'ours' }); const theirs = new Switchboard({ port: channel.port2, name: 'theirs' }); theirs.start(); - channel.port2.onmessageerror = jest.fn(); + channel.port2.onmessageerror = vi.fn(); ours.emit('fakemethod'); await new Promise(setImmediate); expect(channel.port2.onmessageerror).not.toHaveBeenCalled(); @@ -277,7 +277,7 @@ describe('comms', () => { }); theirs.start(); - console.error = jest.fn(); // will be restored by the afterEach + console.error = vi.fn(); // will be restored by the afterEach await expect(ours.get('failing')).rejects.toThrow( '[theirs] Method "failing" threw an error', ); diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/test/WorldMap.test.ts b/superset-frontend/plugins/legacy-plugin-chart-world-map/test/WorldMap.test.ts index 096c558bc63..1ca3ca3c76c 100644 --- a/superset-frontend/plugins/legacy-plugin-chart-world-map/test/WorldMap.test.ts +++ b/superset-frontend/plugins/legacy-plugin-chart-world-map/test/WorldMap.test.ts @@ -21,6 +21,7 @@ import d3 from 'd3'; import { getNumberFormatter, ValueFormatter } from '@superset-ui/core'; import WorldMap from '../src/WorldMap'; import { ColorBy } from '../src/utils'; +import { afterEach, beforeEach, expect, test, vi, type Mock } from 'vitest'; interface WorldMapDataEntry { country: string; @@ -61,24 +62,24 @@ interface WorldMapProps { type MouseEventHandler = (this: HTMLElement) => void; interface MockD3Selection { - attr: jest.Mock; - style: jest.Mock; - classed: jest.Mock; - selectAll: jest.Mock; + attr: Mock; + style: Mock; + classed: Mock; + selectAll: Mock; } // Mock Datamap -const mockBubbles = jest.fn(); -const mockUpdateChoropleth = jest.fn(); +const mockBubbles = vi.fn(); +const mockUpdateChoropleth = vi.fn(); const mockSvg = { - selectAll: jest.fn().mockReturnThis(), - on: jest.fn().mockReturnThis(), - attr: jest.fn().mockReturnThis(), - style: jest.fn().mockReturnThis(), + selectAll: vi.fn().mockReturnThis(), + on: vi.fn().mockReturnThis(), + attr: vi.fn().mockReturnThis(), + style: vi.fn().mockReturnThis(), }; -jest.mock('datamaps/dist/datamaps.all.min', () => - jest.fn().mockImplementation(config => { +vi.mock('datamaps/dist/datamaps.all.min', () => ({ + default: vi.fn().mockImplementation(function (config) { // Call the done callback immediately to simulate Datamap initialization if (config.done) { config.done({ @@ -91,7 +92,7 @@ jest.mock('datamaps/dist/datamaps.all.min', () => svg: mockSvg, }; }), -); +})); let container: HTMLElement; const formatter = getNumberFormatter(); @@ -134,8 +135,8 @@ const baseProps: WorldMapProps = { }, countryFieldtype: 'code', entity: 'country', - onContextMenu: jest.fn(), - setDataMask: jest.fn(), + onContextMenu: vi.fn(), + setDataMask: vi.fn(), inContextMenu: false, filterState: { selectedValues: [] }, emitCrossFilters: false, @@ -143,7 +144,7 @@ const baseProps: WorldMapProps = { }; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); container = document.createElement('div'); document.body.appendChild(container); }); @@ -177,7 +178,7 @@ test('stores original fill color on mouseover', () => { // Mock d3.select to return the mock element const mockD3Selection: MockD3Selection = { - attr: jest.fn((attrName: string, value?: string) => { + attr: vi.fn((attrName: string, value?: string) => { if (value !== undefined) { mockElement.setAttribute(attrName, value); } else { @@ -185,7 +186,7 @@ test('stores original fill color on mouseover', () => { } return mockD3Selection; }), - style: jest.fn((styleName: string, value?: string) => { + style: vi.fn((styleName: string, value?: string) => { if (value !== undefined) { mockElement.style[styleName as any] = value; } else { @@ -193,11 +194,11 @@ test('stores original fill color on mouseover', () => { } return mockD3Selection; }), - classed: jest.fn().mockReturnThis(), - selectAll: jest.fn().mockReturnValue({ remove: jest.fn() }), + classed: vi.fn().mockReturnThis(), + selectAll: vi.fn().mockReturnValue({ remove: vi.fn() }), }; - jest.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); + vi.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); // Capture the mouseover handler mockSvg.on.mockImplementation((event: string, handler: MouseEventHandler) => { @@ -231,7 +232,7 @@ test('restores original fill color on mouseout for country with data', () => { let mouseoutHandler: MouseEventHandler | null = null; const mockD3Selection: MockD3Selection = { - attr: jest.fn((attrName: string, value?: string | null) => { + attr: vi.fn((attrName: string, value?: string | null) => { if (value !== undefined) { if (value === null) { mockElement.removeAttribute(attrName); @@ -242,17 +243,17 @@ test('restores original fill color on mouseout for country with data', () => { } return mockElement.getAttribute(attrName); }), - style: jest.fn((styleName: string, value?: string) => { + style: vi.fn((styleName: string, value?: string) => { if (value !== undefined) { mockElement.style[styleName as any] = value; } return mockElement.style[styleName as any] || mockD3Selection; }), - classed: jest.fn().mockReturnThis(), - selectAll: jest.fn().mockReturnValue({ remove: jest.fn() }), + classed: vi.fn().mockReturnThis(), + selectAll: vi.fn().mockReturnValue({ remove: vi.fn() }), }; - jest.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); + vi.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); // Capture the mouseout handler mockSvg.on.mockImplementation((event: string, handler: MouseEventHandler) => { @@ -287,7 +288,7 @@ test('restores default fill color on mouseout for country with no data', () => { let mouseoutHandler: MouseEventHandler | null = null; const mockD3Selection: MockD3Selection = { - attr: jest.fn((attrName: string, value?: string | null) => { + attr: vi.fn((attrName: string, value?: string | null) => { if (value !== undefined) { if (value === null) { mockElement.removeAttribute(attrName); @@ -298,17 +299,17 @@ test('restores default fill color on mouseout for country with no data', () => { } return mockElement.getAttribute(attrName); }), - style: jest.fn((styleName: string, value?: string) => { + style: vi.fn((styleName: string, value?: string) => { if (value !== undefined) { mockElement.style[styleName as any] = value; } return mockElement.style[styleName as any] || mockD3Selection; }), - classed: jest.fn().mockReturnThis(), - selectAll: jest.fn().mockReturnValue({ remove: jest.fn() }), + classed: vi.fn().mockReturnThis(), + selectAll: vi.fn().mockReturnValue({ remove: vi.fn() }), }; - jest.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); + vi.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); mockSvg.on.mockImplementation((event: string, handler: MouseEventHandler) => { if (event === 'mouseout') { @@ -344,13 +345,13 @@ test('does not handle mouse events when inContextMenu is true', () => { let mouseoutHandler: MouseEventHandler | null = null; const mockD3Selection: MockD3Selection = { - attr: jest.fn(() => mockD3Selection), - style: jest.fn(() => mockD3Selection), - classed: jest.fn().mockReturnThis(), - selectAll: jest.fn().mockReturnValue({ remove: jest.fn() }), + attr: vi.fn(() => mockD3Selection), + style: vi.fn(() => mockD3Selection), + classed: vi.fn().mockReturnThis(), + selectAll: vi.fn().mockReturnValue({ remove: vi.fn() }), }; - jest.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); + vi.spyOn(d3 as any, 'select').mockReturnValue(mockD3Selection as any); mockSvg.on.mockImplementation((event: string, handler: MouseEventHandler) => { if (event === 'mouseover') { diff --git a/superset-frontend/plugins/legacy-plugin-chart-world-map/test/tsconfig.json b/superset-frontend/plugins/legacy-plugin-chart-world-map/test/tsconfig.json deleted file mode 100644 index 4c9211140ce..00000000000 --- a/superset-frontend/plugins/legacy-plugin-chart-world-map/test/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": "../../../tsconfig.options.json", - "include": ["**/*"], - "compilerOptions": { - "esModuleInterop": true, - "types": ["jest", "node"] - } -} diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts index e1185ca5f0f..bf62be4ad06 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Contour/getSafeCellSize.test.ts @@ -54,7 +54,7 @@ describe('getSafeCellSize', () => { }); test('calls onAutoAdjust when scaling happens', () => { - const spy = jest.fn(); + const spy = vi.fn(); getSafeCellSize({ cellSize: 1, diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts index bf3b8527df8..f268216e744 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Geojson/Geojson.test.ts @@ -24,7 +24,7 @@ import { computeGeoJsonIconOptionsFromFormData, } from './Geojson'; -jest.mock('@deck.gl/react', () => ({ +vi.mock('@deck.gl/react', () => ({ __esModule: true, default: () => null, })); diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts index a7b7be3ab9a..6d805812a0c 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Polygon/transformProps.test.ts @@ -27,9 +27,9 @@ interface PolygonFeature { metrics?: Record; } -jest.mock('../spatialUtils', () => ({ - ...jest.requireActual('../spatialUtils'), - getMapboxApiKey: jest.fn(() => 'mock-mapbox-key'), +vi.mock('../spatialUtils', async importActual => ({ + ...(await importActual()), + getMapboxApiKey: vi.fn(() => 'mock-mapbox-key'), })); describe('Polygon transformProps', () => { diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts index bbdf7ced7bf..825e336ce47 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/Scatter/transformProps.test.ts @@ -28,9 +28,9 @@ interface ScatterFeature { extraProps?: Record; } -jest.mock('../spatialUtils', () => ({ - ...jest.requireActual('../spatialUtils'), - getMapboxApiKey: jest.fn(() => 'mock-mapbox-key'), +vi.mock('../spatialUtils', async importActual => ({ + ...(await importActual()), + getMapboxApiKey: vi.fn(() => 'mock-mapbox-key'), })); const mockChartProps: Partial = { diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts index 1dda87eb046..e6d9931b381 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/common.test.ts @@ -97,11 +97,11 @@ describe('getAggFunc', () => { }); describe('commonLayerProps', () => { - const mockSetTooltip = jest.fn(); - const mockSetTooltipContent = jest.fn( + const mockSetTooltip = vi.fn(); + const mockSetTooltipContent = vi.fn( () => (o: JsonObject) => `Tooltip for ${o}`, ); - const mockOnSelect = jest.fn(); + const mockOnSelect = vi.fn(); test('returns correct props when js_tooltip is provided', () => { const formData = { diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts index d9329760cc9..e611110cf88 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/spatialUtils.test.ts @@ -32,17 +32,20 @@ import { transformSpatialProps, SpatialFormData, } from './spatialUtils'; +import { Mock } from 'vitest'; -jest.mock('ngeohash', () => ({ - decode: jest.fn(), +vi.mock('ngeohash', () => ({ + decode: vi.fn(), })); -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - buildQueryContext: jest.fn(), - getMetricLabel: jest.fn(), - ensureIsArray: jest.fn(arr => arr || []), - normalizeOrderBy: jest.fn(({ orderby }) => ({ orderby })), +const { mockBuildQueryContext, mockGetMetricLabel } = vi.hoisted(() => ({ mockBuildQueryContext: vi.fn(), mockGetMetricLabel: vi.fn() })); + +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + buildQueryContext: mockBuildQueryContext, + getMetricLabel: mockGetMetricLabel, + ensureIsArray: vi.fn(arr => arr || []), + normalizeOrderBy: vi.fn(({ orderby }) => ({ orderby })), })); // Mock DOM element for bootstrap data @@ -55,13 +58,13 @@ const mockBootstrapData = { }; Object.defineProperty(document, 'getElementById', { - value: jest.fn().mockReturnValue({ - getAttribute: jest.fn().mockReturnValue(JSON.stringify(mockBootstrapData)), + value: vi.fn().mockReturnValue({ + getAttribute: vi.fn().mockReturnValue(JSON.stringify(mockBootstrapData)), }), writable: true, }); -const mockDecode = decode as jest.MockedFunction; +const mockDecode = decode as Mock; describe('spatialUtils', () => { test('getSpatialColumns returns correct columns for latlong type', () => { @@ -190,8 +193,6 @@ describe('spatialUtils', () => { }); test('buildSpatialQuery calls buildQueryContext with correct parameters', () => { - const mockBuildQueryContext = - jest.requireMock('@superset-ui/core').buildQueryContext; const formData: SpatialFormData = { spatial: { type: 'latlong', @@ -417,8 +418,6 @@ describe('spatialUtils', () => { }); test('transformSpatialProps transforms chart props correctly', () => { - const mockGetMetricLabel = - jest.requireMock('@superset-ui/core').getMetricLabel; mockGetMetricLabel.mockReturnValue('count_label'); const chartProps: ChartProps = { @@ -432,10 +431,10 @@ describe('spatialUtils', () => { height: 400, width: 600, hooks: { - onAddFilter: jest.fn(), - onContextMenu: jest.fn(), - setControlValue: jest.fn(), - setDataMask: jest.fn(), + onAddFilter: vi.fn(), + onContextMenu: vi.fn(), + setControlValue: vi.fn(), + setDataMask: vi.fn(), }, queriesData: [ { @@ -557,8 +556,6 @@ describe('spatialUtils', () => { }); test('transformSpatialProps handles missing metric', () => { - const mockGetMetricLabel = - jest.requireMock('@superset-ui/core').getMetricLabel; mockGetMetricLabel.mockReturnValue(undefined); const chartProps: ChartProps = { diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts index 211bd9e6fd8..0064079d476 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/transformUtils.test.ts @@ -20,13 +20,13 @@ import { getMetricLabel } from '@superset-ui/core'; import { getMetricLabelFromFormData, parseMetricValue } from './transformUtils'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getMetricLabel: jest.fn((metric: string) => metric), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getMetricLabel: vi.fn((metric: string) => metric), })); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('getMetricLabelFromFormData should return undefined for undefined input', () => { diff --git a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts index dfd5878e54f..a33e7a0a697 100644 --- a/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts +++ b/superset-frontend/plugins/legacy-preset-chart-deckgl/src/layers/utils/metricUtils.test.ts @@ -26,13 +26,13 @@ import { getFixedValue, } from './metricUtils'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getMetricLabel: jest.fn((metric: string) => metric), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getMetricLabel: vi.fn((metric: string) => metric), })); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('isMetricValue should identify metric values correctly', () => { diff --git a/superset-frontend/plugins/plugin-chart-ag-grid-table/test/utils/filterStateManager.test.ts b/superset-frontend/plugins/plugin-chart-ag-grid-table/test/utils/filterStateManager.test.ts index 92ef3b05e9a..45e22320698 100644 --- a/superset-frontend/plugins/plugin-chart-ag-grid-table/test/utils/filterStateManager.test.ts +++ b/superset-frontend/plugins/plugin-chart-ag-grid-table/test/utils/filterStateManager.test.ts @@ -62,8 +62,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -97,8 +97,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -130,8 +130,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -174,8 +174,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -209,8 +209,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -260,8 +260,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn((colId: string) => { + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn((colId: string) => { if (colId === 'name') return Promise.resolve(mockFilterInstanceName); if (colId === 'age') return Promise.resolve(mockFilterInstanceAge); if (colId === 'status') @@ -298,8 +298,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -318,8 +318,8 @@ describe('filterStateManager', () => { const filterModel = {}; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -350,8 +350,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -372,8 +372,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -403,8 +403,8 @@ describe('filterStateManager', () => { let callCount = 0; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn((colId: string) => { + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn((colId: string) => { callCount += 1; // Return match on col2 if (colId === 'col2') { @@ -443,8 +443,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -463,8 +463,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => originalFilterModel), - getColumnFilterInstance: jest.fn(() => Promise.resolve(null)), + getFilterModel: vi.fn(() => originalFilterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(null)), }; const gridRef = { @@ -501,8 +501,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -548,8 +548,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -590,8 +590,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; @@ -633,8 +633,8 @@ describe('filterStateManager', () => { }; const mockApi = { - getFilterModel: jest.fn(() => filterModel), - getColumnFilterInstance: jest.fn(() => + getFilterModel: vi.fn(() => filterModel), + getColumnFilterInstance: vi.fn(() => Promise.resolve(mockFilterInstance), ), }; diff --git a/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/buildQuery.test.ts b/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/buildQuery.test.ts index a4132d25195..8e1ab0a685d 100644 --- a/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/buildQuery.test.ts +++ b/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/buildQuery.test.ts @@ -39,9 +39,9 @@ describe('CartodiagramPlugin buildQuery', () => { geom_column: 'geom', }; - let chartQueryBuilderMock: jest.MockedFunction; + let chartQueryBuilderMock: vi.MockedFunction; beforeEach(() => { - chartQueryBuilderMock = jest.fn(); + chartQueryBuilderMock = vi.fn(); const registry = getChartBuildQueryRegistry(); registry.registerValue('pie', chartQueryBuilderMock); diff --git a/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/transformProps.test.ts index e59b46955c1..210bb3c833e 100644 --- a/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-cartodiagram/test/plugin/transformProps.test.ts @@ -94,11 +94,11 @@ describe('CartodiagramPlugin transformProps', () => { theme: supersetTheme, }); - let chartTransformPropsPieMock: jest.MockedFunction; - let chartTransformPropsTimeseriesMock: jest.MockedFunction; + let chartTransformPropsPieMock: vi.MockedFunction; + let chartTransformPropsTimeseriesMock: vi.MockedFunction; beforeEach(() => { - chartTransformPropsPieMock = jest.fn(); - chartTransformPropsTimeseriesMock = jest.fn(); + chartTransformPropsPieMock = vi.fn(); + chartTransformPropsTimeseriesMock = vi.fn(); const registry = getChartTransformPropsRegistry(); registry.registerValue('pie', chartTransformPropsPieMock); registry.registerValue( diff --git a/superset-frontend/plugins/plugin-chart-cartodiagram/test/util/transformPropsUtil.test.ts b/superset-frontend/plugins/plugin-chart-cartodiagram/test/util/transformPropsUtil.test.ts index 4558b6b946d..775940d28fc 100644 --- a/superset-frontend/plugins/plugin-chart-cartodiagram/test/util/transformPropsUtil.test.ts +++ b/superset-frontend/plugins/plugin-chart-cartodiagram/test/util/transformPropsUtil.test.ts @@ -175,7 +175,7 @@ describe('transformPropsUtil', () => { }); describe('getChartConfigs', () => { - let chartTransformer: jest.MockedFunction; + let chartTransformer: vi.MockedFunction; const geomColumn = 'geom'; const pieChartConfig = { params: {}, @@ -189,7 +189,7 @@ describe('transformPropsUtil', () => { ], }; beforeEach(() => { - chartTransformer = jest.fn(); + chartTransformer = vi.fn(); }); test('calls the transformProps function for every location', () => { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts index 7d8e74156d4..69e11d5cd87 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/controlPanel.test.ts @@ -22,15 +22,15 @@ import controlPanel from './controlPanel'; const { __mockShiftMetric } = ChartControls as any; -jest.mock('@superset-ui/core', () => ({ +vi.mock('@superset-ui/core', () => ({ GenericDataType: { Numeric: 'numeric' }, SMART_DATE_ID: 'SMART_DATE_ID', t: (str: string) => str, })); -jest.mock('@superset-ui/chart-controls', () => { +vi.mock('@superset-ui/chart-controls', () => { // Define the mock function inside the factory - const mockShiftMetric = jest.fn(() => 'shiftedMetric'); + const mockShiftMetric = vi.fn(() => 'shiftedMetric'); return { ControlPanelConfig: {}, D3_FORMAT_DOCS: 'Format docs', diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts index 76a3cbde936..20f6483b56a 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberTotal/transformProps.test.ts @@ -22,28 +22,28 @@ import { getColorFormatters } from '@superset-ui/chart-controls'; import { BigNumberTotalChartProps } from '../types'; import transformProps from './transformProps'; -jest.mock('@superset-ui/chart-controls', () => ({ - getColorFormatters: jest.fn(), +vi.mock('@superset-ui/chart-controls', () => ({ + getColorFormatters: vi.fn(), })); -jest.mock('@superset-ui/core', () => ({ +vi.mock('@superset-ui/core', () => ({ GenericDataType: { Temporal: 2, String: 1 }, - getMetricLabel: jest.fn(metric => metric), - extractTimegrain: jest.fn(() => 'P1D'), - getValueFormatter: jest.fn(() => (v: any) => `$${v}`), + getMetricLabel: vi.fn(metric => metric), + extractTimegrain: vi.fn(() => 'P1D'), + getValueFormatter: vi.fn(() => (v: any) => `$${v}`), })); -jest.mock('../utils', () => ({ - getDateFormatter: jest.fn(() => (v: any) => `${v}pm`), - parseMetricValue: jest.fn(val => Number(val)), - getOriginalLabel: jest.fn((metric, metrics) => { +vi.mock('../utils', () => ({ + getDateFormatter: vi.fn(() => (v: any) => `${v}pm`), + parseMetricValue: vi.fn(val => Number(val)), + getOriginalLabel: vi.fn((metric, metrics) => { console.log(metrics); return metric; }), })); describe('BigNumberTotal transformProps', () => { - const onContextMenu = jest.fn(); + const onContextMenu = vi.fn(); const baseFormData = { headerFontSize: 20, metric: 'value', @@ -112,7 +112,7 @@ describe('BigNumberTotal transformProps', () => { height: 300, queriesData: [{ data: [], coltypes: [] }], rawFormData: { dummy: 'raw' }, - hooks: { onContextMenu: jest.fn() }, + hooks: { onContextMenu: vi.fn() }, datasource: { currencyFormats: { value: '$0,0.00' }, columnFormats: { value: '$0,0.00' }, @@ -234,7 +234,7 @@ describe('BigNumberTotal transformProps', () => { test('should propagate colorThresholdFormatters from getColorFormatters', () => { // Override the getColorFormatters mock to return specific value const mockFormatters = [{ formatter: 'red' }]; - (getColorFormatters as jest.Mock).mockReturnValueOnce(mockFormatters); + (getColorFormatters as vi.Mock).mockReturnValueOnce(mockFormatters); const chartProps = { width: 800, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.test.ts index 0ce7743b362..cbf9463d762 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/buildQuery.test.ts @@ -19,15 +19,15 @@ import { QueryFormData } from '@superset-ui/core'; import buildQuery from './buildQuery'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getXAxisColumn: jest.fn(() => 'order_date'), - isXAxisSet: jest.fn(() => true), +vi.mock('@superset-ui/core', async (importActual) => ({ + ...(await importActual()), + getXAxisColumn: vi.fn(() => 'order_date'), + isXAxisSet: vi.fn(() => true), })); -jest.mock('@superset-ui/chart-controls', () => ({ - pivotOperator: jest.fn(() => ({ operation: 'pivot' })), - aggregationOperator: jest.fn(formData => { +vi.mock('@superset-ui/chart-controls', () => ({ + pivotOperator: vi.fn(() => ({ operation: 'pivot' })), + aggregationOperator: vi.fn(formData => { if (formData.aggregation === 'LAST_VALUE' || !formData.aggregation) { return undefined; } @@ -36,9 +36,9 @@ jest.mock('@superset-ui/chart-controls', () => ({ options: { operator: formData.aggregation }, }; }), - flattenOperator: jest.fn(() => ({ operation: 'flatten' })), - resampleOperator: jest.fn(() => ({ operation: 'resample' })), - rollingWindowOperator: jest.fn(() => ({ operation: 'rolling' })), + flattenOperator: vi.fn(() => ({ operation: 'flatten' })), + resampleOperator: vi.fn(() => ({ operation: 'resample' })), + rollingWindowOperator: vi.fn(() => ({ operation: 'rolling' })), })); describe('BigNumberWithTrendline buildQuery', () => { diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts index aa0dcdcb6b0..842693db348 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/BigNumber/BigNumberWithTrendline/transformProps.test.ts @@ -21,7 +21,7 @@ import transformProps from './transformProps'; import { BigNumberWithTrendlineChartProps, BigNumberDatum } from '../types'; // Mock chart-controls to avoid styled-components issues in Jest -jest.mock('@superset-ui/chart-controls', () => ({ +vi.mock('@superset-ui/chart-controls', () => ({ aggregationChoices: { raw: { label: 'Force server-side aggregation', @@ -55,40 +55,40 @@ jest.mock('@superset-ui/chart-controls', () => ({ }, })); -jest.mock('@superset-ui/core', () => ({ +vi.mock('@superset-ui/core', () => ({ GenericDataType: { Temporal: 2, String: 1 }, - extractTimegrain: jest.fn(() => 'P1D'), - getMetricLabel: jest.fn(metric => metric), - getXAxisLabel: jest.fn(() => '__timestamp'), - getValueFormatter: jest.fn(() => ({ + extractTimegrain: vi.fn(() => 'P1D'), + getMetricLabel: vi.fn(metric => metric), + getXAxisLabel: vi.fn(() => '__timestamp'), + getValueFormatter: vi.fn(() => ({ format: (v: number) => `$${v}`, })), - getNumberFormatter: jest.fn(() => (v: number) => `${(v * 100).toFixed(1)}%`), - t: jest.fn(v => v), - tooltipHtml: jest.fn(() => '
tooltip
'), + getNumberFormatter: vi.fn(() => (v: number) => `${(v * 100).toFixed(1)}%`), + t: vi.fn(v => v), + tooltipHtml: vi.fn(() => '
tooltip
'), NumberFormats: { PERCENT_SIGNED_1_POINT: '.1%', }, })); -jest.mock('../utils', () => ({ - getDateFormatter: jest.fn(() => (v: any) => `${v}pm`), - parseMetricValue: jest.fn(val => Number(val)), - getOriginalLabel: jest.fn((metric, metrics) => { +vi.mock('../utils', () => ({ + getDateFormatter: vi.fn(() => (v: any) => `${v}pm`), + parseMetricValue: vi.fn(val => Number(val)), + getOriginalLabel: vi.fn((metric, metrics) => { console.log(metrics); return metric; }), })); -jest.mock('../../utils/tooltip', () => ({ - getDefaultTooltip: jest.fn(() => ({})), +vi.mock('../../utils/tooltip', () => ({ + getDefaultTooltip: vi.fn(() => ({})), })); -jest.mock('../../utils/formatters', () => ({ - getXAxisFormatter: jest.fn(() => String), +vi.mock('../../utils/formatters', () => ({ + getXAxisFormatter: vi.fn(() => String), })); -jest.mock('../../constants', () => ({ +vi.mock('../../constants', () => ({ TIMESERIES_CONSTANTS: { gridOffsetBottom: 20, gridOffsetLeft: 20, @@ -98,7 +98,7 @@ jest.mock('../../constants', () => ({ })); describe('BigNumberWithTrendline transformProps', () => { - const onContextMenu = jest.fn(); + const onContextMenu = vi.fn(); const baseFormData = { headerFontSize: 20, metric: 'value', diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/controlPanel.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/controlPanel.test.ts index 2b56d6ef0ad..b4f33412a43 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/controlPanel.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/Bar/controlPanel.test.ts @@ -46,13 +46,13 @@ const getControl = (controlName: string) => { }; // Mock getStandardizedControls -jest.mock('@superset-ui/chart-controls', () => { - const actual = jest.requireActual('@superset-ui/chart-controls'); +vi.mock('@superset-ui/chart-controls', async importActual => { + const actual = await importActual(); return { - ...actual, - getStandardizedControls: jest.fn(() => ({ - popAllMetrics: jest.fn(() => []), - popAllColumns: jest.fn(() => []), + ...(actual as any), + getStandardizedControls: vi.fn(() => ({ + popAllMetrics: vi.fn(() => []), + popAllColumns: vi.fn(() => []), })), }; }); diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformers.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformers.test.ts index 68f48196df2..6dbae79c133 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformers.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/transformers.test.ts @@ -35,7 +35,7 @@ import transformProps from '../../src/Timeseries/transformProps'; import * as seriesUtils from '../../src/utils/series'; // Mock the colorScale function to return different colors based on key -const mockColorScale = jest.fn((key: string) => { +const mockColorScale = vi.fn((key: string) => { if (key === 'test-key') return '#1f77b4'; // blue if (key === 'series-key') return '#ff7f0e'; // orange return '#2ca02c'; // green for any other key @@ -289,9 +289,9 @@ test('should configure time axis labels to show max label for last month visibil ); }); -function setupGetChartPaddingMock(): jest.SpyInstance { +function setupGetChartPaddingMock(): vi.SpyInstance { // Mock getChartPadding to return the padding object as-is for easier testing - const getChartPaddingSpy = jest.spyOn(seriesUtils, 'getChartPadding'); + const getChartPaddingSpy = vi.spyOn(seriesUtils, 'getChartPadding'); getChartPaddingSpy.mockImplementation( ( show: boolean, diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts index 467c58f0fd8..82b290c4b37 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts @@ -525,7 +525,7 @@ describe('extractSeries', () => { ], }, ], - [], + [undefined, undefined, undefined], 5, ]); }); diff --git a/superset-frontend/plugins/plugin-chart-handlebars/package.json b/superset-frontend/plugins/plugin-chart-handlebars/package.json index fd990ed955b..ec0a0d5fd9d 100644 --- a/superset-frontend/plugins/plugin-chart-handlebars/package.json +++ b/superset-frontend/plugins/plugin-chart-handlebars/package.json @@ -43,8 +43,6 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@types/jest": "^30.0.0", - "@types/lodash": "^4.17.23", - "jest": "^30.2.0" + "@types/lodash": "^4.17.23" } } diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/package.json b/superset-frontend/plugins/plugin-chart-pivot-table/package.json index 15658cf9613..cd07e33e69c 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/package.json +++ b/superset-frontend/plugins/plugin-chart-pivot-table/package.json @@ -38,8 +38,6 @@ "react-dom": "^17.0.2" }, "devDependencies": { - "@babel/types": "^7.29.0", - "@types/jest": "^30.0.0", - "jest": "^30.2.0" + "@babel/types": "^7.29.0" } } diff --git a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts index 5f243b04e4e..9245e0c0560 100644 --- a/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts +++ b/superset-frontend/plugins/plugin-chart-pivot-table/test/plugin/transformProps.test.ts @@ -22,7 +22,7 @@ import { supersetTheme } from '@apache-superset/core/ui'; import transformProps from '../../src/plugin/transformProps'; import { MetricsLayoutEnum } from '../../src/types'; -const setDataMask = jest.fn(); +const setDataMask = vi.fn(); const formData = { groupbyRows: ['row1', 'row2'], groupbyColumns: ['col1', 'col2'], diff --git a/superset-frontend/spec/helpers/setup.jest.ts b/superset-frontend/spec/helpers/setup.jest.ts new file mode 100644 index 00000000000..5d75c63dbdd --- /dev/null +++ b/superset-frontend/spec/helpers/setup.jest.ts @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import './shim.jest'; +// eslint-disable-next-line no-restricted-syntax -- whole React import is required for mocking React module in tests. +import React from 'react'; +// eslint-disable-next-line no-restricted-imports +import { configure as configureTestingLibrary } from '@testing-library/react'; +import { matchers } from '@emotion/jest'; + +configureTestingLibrary({ + testIdAttribute: 'data-test', +}); + +document.body.innerHTML = '
'; +// expect.extend(matchers); + +// Allow JSX tests to have React import readily available +global.React = React; + +// Mock ace-builds globally for tests +jest.mock('ace-builds/src-min-noconflict/mode-handlebars', () => ({})); +jest.mock('ace-builds/src-min-noconflict/mode-css', () => ({})); +jest.mock('ace-builds/src-noconflict/theme-github', () => ({})); +jest.mock('ace-builds/src-noconflict/theme-monokai', () => ({})); diff --git a/superset-frontend/spec/helpers/setup.ts b/superset-frontend/spec/helpers/setup.ts index 9ec6d6661fe..c4573c5daa9 100644 --- a/superset-frontend/spec/helpers/setup.ts +++ b/superset-frontend/spec/helpers/setup.ts @@ -21,20 +21,28 @@ import './shim'; import React from 'react'; // eslint-disable-next-line no-restricted-imports import { configure as configureTestingLibrary } from '@testing-library/react'; -import { matchers } from '@emotion/jest'; +// import { matchers } from '@emotion/jest'; +import { vi } from 'vitest'; configureTestingLibrary({ testIdAttribute: 'data-test', }); document.body.innerHTML = '
'; -expect.extend(matchers); +// expect.extend(matchers); // Allow JSX tests to have React import readily available global.React = React; +// @ts-ignore +global.setInterval = global.jsdom.window.setInterval; +// @ts-ignore +global.TextEncoder = global.jsdom.window.TextEncoder; +// @ts-ignore +global.TextDecoder = global.jsdom.window.TextDecoder; + // Mock ace-builds globally for tests -jest.mock('ace-builds/src-min-noconflict/mode-handlebars', () => ({})); -jest.mock('ace-builds/src-min-noconflict/mode-css', () => ({})); -jest.mock('ace-builds/src-noconflict/theme-github', () => ({})); -jest.mock('ace-builds/src-noconflict/theme-monokai', () => ({})); +vi.mock('ace-builds/src-min-noconflict/mode-handlebars', () => ({})); +vi.mock('ace-builds/src-min-noconflict/mode-css', () => ({})); +vi.mock('ace-builds/src-noconflict/theme-github', () => ({})); +vi.mock('ace-builds/src-noconflict/theme-monokai', () => ({})); diff --git a/superset-frontend/spec/helpers/shim.jest.tsx b/superset-frontend/spec/helpers/shim.jest.tsx new file mode 100644 index 00000000000..22492b2e682 --- /dev/null +++ b/superset-frontend/spec/helpers/shim.jest.tsx @@ -0,0 +1,146 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { AriaAttributes } from 'react'; +import 'core-js/stable'; +import 'regenerator-runtime/runtime'; +import jQuery from 'jquery'; +// https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options +// in order to mock modules in test case, so avoid absolute import module +import { configure as configureTranslation } from '@apache-superset/core/ui'; +import fetchMock from 'fetch-mock'; +import { Worker } from './Worker'; +import { IntersectionObserver } from './IntersectionObserver'; +import { ResizeObserver } from './ResizeObserver'; +import setupSupersetClient from './setupSupersetClient'; +import CacheStorage from './CacheStorage'; +import { TextEncoder, TextDecoder } from 'util'; + +const exposedProperties = ['window', 'navigator', 'document']; + +const { defaultView } = document; +if (defaultView != null) { + Object.keys(defaultView).forEach(property => { + if (typeof global[property as keyof typeof global] === 'undefined') { + exposedProperties.push(property); + // @ts-ignore due to string-type index signature doesn't apply for `typeof globalThis`. + global[property] = defaultView[property as keyof typeof defaultView]; + } + }); +} + +fetchMock.mockGlobal(); +fetchMock.config.allowRelativeUrls = true; + +const g = global as any; +g.window ??= Object.create(window); +g.window.location ??= { href: 'about:blank' }; +g.window.performance ??= { now: () => new Date().getTime() }; +g.window.Worker ??= Worker; +g.window.IntersectionObserver ??= IntersectionObserver; +g.window.ResizeObserver ??= ResizeObserver; +g.window.featureFlags ??= {}; +g.URL.createObjectURL ??= () => ''; +g.caches = new CacheStorage(); + +// Add shims for TextEncoder and TextDecoder after upgrading jspdf to v3.0.2+ +// Source: https://github.com/parallax/jsPDF/issues/3882 +// g.TextDecoder = TextDecoder; +// g.TextEncoder = TextEncoder; + +Object.defineProperty(window, 'matchMedia', { + writable: true, + value: jest.fn().mockImplementation(query => ({ + matches: false, + media: query, + onchange: null, + addListener: jest.fn(), // Deprecated + removeListener: jest.fn(), // Deprecated + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +g.$ = jQuery(g.window); + +configureTranslation(); +setupSupersetClient(); + +// The useTabId hook depends on BroadcastChannel. Jest has a memory leak problem when +// dealing with native modules. See https://chanind.github.io/javascript/2019/10/12/jest-tests-memory-leak.html +// and https://github.com/facebook/jest/issues/6814 for more information. +jest.mock('src/hooks/useTabId', () => ({ + useTabId: () => 1, +})); + +// Check https://github.com/remarkjs/react-markdown/issues/635 +jest.mock('react-markdown', () => ({ + default: (props: any) => <>{props.children}, +})); +jest.mock('rehype-sanitize', () => ({ default: () => jest.fn() })); +jest.mock('rehype-raw', () => ({ default: () => jest.fn() })); + +// Mocks the Icon component due to its async nature +// Tests should override this when needed +jest.mock('@superset-ui/core/components/Icons/AsyncIcon', () => ({ + default: ({ + fileName, + role, + 'aria-label': ariaLabel, + onClick, + ...rest + }: { + fileName: string; + role?: string; + 'aria-label'?: AriaAttributes['aria-label']; + onClick?: () => void; + }) => { + // Simple mock that provides the essential attributes for testing + const label = ariaLabel || fileName?.replace(/_/g, '-').toLowerCase() || ''; + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions + + ); + }, + StyledIcon: ({ + component: Component, + role, + 'aria-label': ariaLabel, + ...rest + }: { + component: React.ComponentType; + role: string; + 'aria-label': AriaAttributes['aria-label']; + }) => ( + + ), +})); + +// process.env.WEBPACK_MODE = 'test'; diff --git a/superset-frontend/spec/helpers/shim.tsx b/superset-frontend/spec/helpers/shim.tsx index 9e49116df42..dfbddc05e97 100644 --- a/superset-frontend/spec/helpers/shim.tsx +++ b/superset-frontend/spec/helpers/shim.tsx @@ -17,8 +17,8 @@ * under the License. */ import { AriaAttributes } from 'react'; -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; +// import 'core-js/stable'; +// import 'regenerator-runtime/runtime'; import jQuery from 'jquery'; // https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options // in order to mock modules in test case, so avoid absolute import module @@ -29,7 +29,8 @@ import { IntersectionObserver } from './IntersectionObserver'; import { ResizeObserver } from './ResizeObserver'; import setupSupersetClient from './setupSupersetClient'; import CacheStorage from './CacheStorage'; -import { TextEncoder, TextDecoder } from 'util'; +// import { TextEncoder, TextDecoder } from 'util'; +import { vi } from 'vitest'; const exposedProperties = ['window', 'navigator', 'document']; @@ -60,20 +61,20 @@ g.caches = new CacheStorage(); // Add shims for TextEncoder and TextDecoder after upgrading jspdf to v3.0.2+ // Source: https://github.com/parallax/jsPDF/issues/3882 -g.TextDecoder = TextDecoder; -g.TextEncoder = TextEncoder; +// g.TextDecoder = TextDecoder; +// g.TextEncoder = TextEncoder; Object.defineProperty(window, 'matchMedia', { writable: true, - value: jest.fn().mockImplementation(query => ({ + value: vi.fn().mockImplementation(query => ({ matches: false, media: query, onchange: null, - addListener: jest.fn(), // Deprecated - removeListener: jest.fn(), // Deprecated - addEventListener: jest.fn(), - removeEventListener: jest.fn(), - dispatchEvent: jest.fn(), + addListener: vi.fn(), // Deprecated + removeListener: vi.fn(), // Deprecated + addEventListener: vi.fn(), + removeEventListener: vi.fn(), + dispatchEvent: vi.fn(), })), }); @@ -85,19 +86,20 @@ setupSupersetClient(); // The useTabId hook depends on BroadcastChannel. Jest has a memory leak problem when // dealing with native modules. See https://chanind.github.io/javascript/2019/10/12/jest-tests-memory-leak.html // and https://github.com/facebook/jest/issues/6814 for more information. -jest.mock('src/hooks/useTabId', () => ({ +vi.mock('src/hooks/useTabId', () => ({ useTabId: () => 1, })); // Check https://github.com/remarkjs/react-markdown/issues/635 -jest.mock('react-markdown', () => (props: any) => <>{props.children}); -jest.mock('rehype-sanitize', () => () => jest.fn()); -jest.mock('rehype-raw', () => () => jest.fn()); +vi.mock('react-markdown', () => ({ + default: (props: any) => <>{props.children}, +})); +vi.mock('rehype-sanitize', () => ({ default: () => vi.fn() })); +vi.mock('rehype-raw', () => ({ default: () => vi.fn() })); // Mocks the Icon component due to its async nature // Tests should override this when needed -jest.mock('@superset-ui/core/components/Icons/AsyncIcon', () => ({ - __esModule: true, +vi.mock('@superset-ui/core/components/Icons/AsyncIcon', () => ({ default: ({ fileName, role, @@ -142,4 +144,4 @@ jest.mock('@superset-ui/core/components/Icons/AsyncIcon', () => ({ ), })); -process.env.WEBPACK_MODE = 'test'; +// process.env.WEBPACK_MODE = 'test'; diff --git a/superset-frontend/src/SqlLab/actions/sqlLab.test.ts b/superset-frontend/src/SqlLab/actions/sqlLab.test.ts index 0de713eae3a..6bbad789588 100644 --- a/superset-frontend/src/SqlLab/actions/sqlLab.test.ts +++ b/superset-frontend/src/SqlLab/actions/sqlLab.test.ts @@ -34,8 +34,9 @@ import { import { SupersetClient, isFeatureEnabled } from '@superset-ui/core'; import { ADD_TOAST } from 'src/components/MessageToasts/actions'; import { ToastType } from '../../components/MessageToasts/types'; +import { vi, type Mock } from 'vitest'; -const isFeatureEnabledMock = isFeatureEnabled as unknown as jest.Mock; +const isFeatureEnabledMock = isFeatureEnabled as unknown as Mock; const query = { ...queryFixture, id: queryId } as any; // Cast fixture to satisfy SqlLabRootState for getState callbacks in thunk tests const typedInitialState = initialState as unknown as SqlLabRootState; @@ -46,17 +47,17 @@ const middlewares = [thunk]; // eslint-disable-next-line @typescript-eslint/no-explicit-any const mockStore = configureMockStore(middlewares); -jest.mock('nanoid', () => ({ +vi.mock('nanoid', () => ({ nanoid: () => 'abcd', })); afterAll(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - isFeatureEnabled: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + isFeatureEnabled: vi.fn(), })); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks @@ -84,1815 +85,1788 @@ describe('getUpToDateQuery', () => { }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks -describe('async actions', () => { - const mockBigNumber = '9223372036854775807'; - const queryEditor = { - ...defaultQueryEditor, - id: 'abcd', - autorun: false, - latestQueryId: null, - sql: 'SELECT *\nFROM\nWHERE', - name: 'Untitled Query 1', +const mockBigNumber = '9223372036854775807'; +const queryEditor = { + ...defaultQueryEditor, + id: 'abcd', + autorun: false, + latestQueryId: null, + sql: 'SELECT *\nFROM\nWHERE', + name: 'Untitled Query 1', +}; + +let dispatch: Mock; +const fetchQueryEndpoint = 'glob:*/api/v1/sqllab/results/*'; +const runQueryEndpoint = 'glob:*/api/v1/sqllab/execute/'; + +beforeEach(() => { + dispatch = vi.fn(); + fetchMock.removeRoute(fetchQueryEndpoint); + fetchMock.get( + fetchQueryEndpoint, + JSON.stringify({ + data: mockBigNumber, + query: { sqlEditorId: 'dfsadfs' }, + }), + { name: fetchQueryEndpoint }, + ); + + fetchMock.removeRoute(runQueryEndpoint); + fetchMock.post(runQueryEndpoint, `{ "data": ${mockBigNumber} }`, { + name: runQueryEndpoint, + }); +}); + +afterEach(() => { + fetchMock.clearHistory(); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('saveQuery', () => { + const saveQueryEndpoint = 'glob:*/api/v1/saved_query/'; + fetchMock.post(saveQueryEndpoint, { results: { json: {} } }); + + const makeRequest = () => { + const request = actions.saveQuery(query, queryId); + return request(dispatch, () => typedInitialState, undefined); }; - let dispatch: jest.Mock; - const fetchQueryEndpoint = 'glob:*/api/v1/sqllab/results/*'; - const runQueryEndpoint = 'glob:*/api/v1/sqllab/execute/'; + test('posts to the correct url', () => { + expect.assertions(1); + + const store = mockStore(initialState); + return store.dispatch(actions.saveQuery(query, queryId)).then(() => { + expect(fetchMock.callHistory.calls(saveQueryEndpoint)).toHaveLength(1); + }); + }); + + test('posts the correct query object', () => { + const store = mockStore(initialState); + return store.dispatch(actions.saveQuery(query, queryId)).then(() => { + const call = fetchMock.callHistory.calls(saveQueryEndpoint)[0]; + const formData = JSON.parse(call.options.body as string); + const mappedQueryToServer = actions.convertQueryToServer(query); + + // The 'id' field is excluded from the POST payload since it's for new queries + expect(formData.id).toBeUndefined(); + Object.keys(mappedQueryToServer).forEach(key => { + if (key !== 'id') { + expect(formData[key]).toBeDefined(); + } + }); + }); + }); + + test('calls 3 dispatch actions', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(dispatch.mock.calls.length).toBe(2); + }); + }); + + test('calls QUERY_EDITOR_SAVED after making a request', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(dispatch.mock.calls[0][0].type).toBe(actions.QUERY_EDITOR_SAVED); + }); + }); + + test('onSave calls QUERY_EDITOR_SAVED and QUERY_EDITOR_SET_TITLE', () => { + expect.assertions(1); + + const store = mockStore(initialState); + const expectedActionTypes = [ + actions.QUERY_EDITOR_SAVED, + actions.QUERY_EDITOR_SET_TITLE, + ]; + return store.dispatch(actions.saveQuery(query, queryId)).then(() => { + expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + }); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('formatQuery', () => { + const formatQueryEndpoint = 'glob:*/api/v1/sqllab/format_sql/'; + const expectedSql = 'SELECT 1'; beforeEach(() => { - dispatch = jest.fn(); + fetchMock.removeRoute(formatQueryEndpoint); + fetchMock.post( + formatQueryEndpoint, + { result: expectedSql }, + { + name: formatQueryEndpoint, + }, + ); + }); + + test('posts to the correct url', async () => { + const store = mockStore(initialState); + store.dispatch(actions.formatQuery(query as unknown as QueryEditor)); + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + expect(store.getActions()[0].type).toBe(actions.QUERY_EDITOR_SET_SQL); + expect(store.getActions()[0].sql).toBe(expectedSql); + }); + + test('sends only sql in request body when no dbId or templateParams', async () => { + const queryEditorWithoutExtras = { + ...defaultQueryEditor, + sql: 'SELECT * FROM table', + dbId: null, + templateParams: null, + }; + const state = { + sqlLab: { + queryEditors: [queryEditorWithoutExtras], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + store.dispatch( + actions.formatQuery(queryEditorWithoutExtras as unknown as QueryEditor), + ); + + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + + const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + + expect(body).toEqual({ sql: 'SELECT * FROM table' }); + expect(body.database_id).toBeUndefined(); + expect(body.template_params).toBeUndefined(); + }); + + test('includes database_id in request when dbId is provided', async () => { + const queryEditorWithDb = { + ...defaultQueryEditor, + sql: 'SELECT * FROM table', + dbId: 5, + templateParams: null, + }; + const state = { + sqlLab: { + queryEditors: [queryEditorWithDb], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + store.dispatch( + actions.formatQuery(queryEditorWithDb as unknown as QueryEditor), + ); + + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + + const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + + expect(body).toEqual({ + sql: 'SELECT * FROM table', + database_id: 5, + }); + }); + + test('includes template_params as string when provided as string', async () => { + const queryEditorWithTemplateString = { + ...defaultQueryEditor, + sql: 'SELECT * FROM table WHERE id = {{ user_id }}', + dbId: 5, + templateParams: '{"user_id": 123}', + }; + const state = { + sqlLab: { + queryEditors: [queryEditorWithTemplateString], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + store.dispatch( + actions.formatQuery( + queryEditorWithTemplateString as unknown as QueryEditor, + ), + ); + + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + + const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + + expect(body).toEqual({ + sql: 'SELECT * FROM table WHERE id = {{ user_id }}', + database_id: 5, + template_params: '{"user_id": 123}', + }); + }); + + test('stringifies template_params when provided as object', async () => { + const queryEditorWithTemplateObject = { + ...defaultQueryEditor, + sql: 'SELECT * FROM table WHERE id = {{ user_id }}', + dbId: 5, + templateParams: { user_id: 123, status: 'active' }, + }; + const state = { + sqlLab: { + queryEditors: [queryEditorWithTemplateObject], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + store.dispatch( + actions.formatQuery( + queryEditorWithTemplateObject as unknown as QueryEditor, + ), + ); + + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + + const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + + expect(body).toEqual({ + sql: 'SELECT * FROM table WHERE id = {{ user_id }}', + database_id: 5, + template_params: '{"user_id":123,"status":"active"}', + }); + }); + + test('dispatches QUERY_EDITOR_SET_SQL with formatted result', async () => { + const formattedSql = 'SELECT\n *\nFROM\n table'; + fetchMock.removeRoute(formatQueryEndpoint); + fetchMock.route( + formatQueryEndpoint, + { result: formattedSql }, + { name: formatQueryEndpoint }, + ); + + const queryEditorToFormat = { + ...defaultQueryEditor, + sql: 'SELECT * FROM table', + }; + const state = { + sqlLab: { + queryEditors: [queryEditorToFormat], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + await store.dispatch(actions.formatQuery(queryEditorToFormat)); + + const dispatchedActions = store.getActions(); + expect(dispatchedActions).toHaveLength(1); + expect(dispatchedActions[0].type).toBe(actions.QUERY_EDITOR_SET_SQL); + expect(dispatchedActions[0].sql).toBe(formattedSql); + }); + + test('uses up-to-date query editor state from store', async () => { + const outdatedQueryEditor = { + ...defaultQueryEditor, + sql: 'OLD SQL', + dbId: 1, + }; + const upToDateQueryEditor = { + ...defaultQueryEditor, + sql: 'SELECT * FROM updated_table', + dbId: 10, + }; + const state = { + sqlLab: { + queryEditors: [upToDateQueryEditor], + unsavedQueryEditor: {}, + }, + }; + const store = mockStore(state); + + // Pass outdated query editor, but expect the function to use the up-to-date one from store + store.dispatch(actions.formatQuery(outdatedQueryEditor)); + + await waitFor(() => + expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength(1), + ); + + const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + + expect(body.sql).toBe('SELECT * FROM updated_table'); + expect(body.database_id).toBe(10); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('fetchQueryResults', () => { + const makeRequest = () => { + const store = mockStore(initialState); + const request = actions.fetchQueryResults(query); + return request(dispatch, store.getState, undefined); + }; + + test('makes the fetch request', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(fetchMock.callHistory.calls(fetchQueryEndpoint)).toHaveLength(1); + }); + }); + + test('calls requestQueryResults', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(dispatch.mock.calls[0][0].type).toBe( + actions.REQUEST_QUERY_RESULTS, + ); + }); + }); + + /* oxlint-disable-next-line jest/no-disabled-tests */ + test.skip('parses large number result without losing precision', () => + makeRequest().then(() => { + expect(fetchMock.callHistory.calls(fetchQueryEndpoint)).toHaveLength(1); + expect(dispatch.mock.calls.length).toBe(2); + expect( + dispatch.mock.calls[1][ + dispatch.mock.calls[1].length - 1 + ].results.data.toString(), + ).toBe(mockBigNumber); + })); + + test('calls querySuccess on fetch success', () => { + expect.assertions(1); + + const store = mockStore({}); + const expectedActionTypes = [ + actions.REQUEST_QUERY_RESULTS, + actions.QUERY_SUCCESS, + ]; + return store.dispatch(actions.fetchQueryResults(query)).then(() => { + expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + }); + }); + + test('calls queryFailed on fetch error', ({ expect }) => { + expect.assertions(1); fetchMock.removeRoute(fetchQueryEndpoint); fetchMock.get( fetchQueryEndpoint, - JSON.stringify({ - data: mockBigNumber, - query: { sqlEditorId: 'dfsadfs' }, - }), + { throws: { message: 'error text' } }, { name: fetchQueryEndpoint }, ); - fetchMock.removeRoute(runQueryEndpoint); - fetchMock.post(runQueryEndpoint, `{ "data": ${mockBigNumber} }`, { - name: runQueryEndpoint, - }); - }); - - afterEach(() => { - fetchMock.clearHistory(); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('saveQuery', () => { - const saveQueryEndpoint = 'glob:*/api/v1/saved_query/'; - fetchMock.post(saveQueryEndpoint, { results: { json: {} } }); - - const makeRequest = () => { - const request = actions.saveQuery(query, queryId); - return request(dispatch, () => typedInitialState, undefined); - }; - - test('posts to the correct url', () => { - expect.assertions(1); - - const store = mockStore(initialState); - return store.dispatch(actions.saveQuery(query, queryId)).then(() => { - expect(fetchMock.callHistory.calls(saveQueryEndpoint)).toHaveLength(1); - }); - }); - - test('posts the correct query object', () => { - const store = mockStore(initialState); - return store.dispatch(actions.saveQuery(query, queryId)).then(() => { - const call = fetchMock.callHistory.calls(saveQueryEndpoint)[0]; - const formData = JSON.parse(call.options.body as string); - const mappedQueryToServer = actions.convertQueryToServer(query); - - // The 'id' field is excluded from the POST payload since it's for new queries - expect(formData.id).toBeUndefined(); - Object.keys(mappedQueryToServer).forEach(key => { - if (key !== 'id') { - expect(formData[key]).toBeDefined(); - } - }); - }); - }); - - test('calls 3 dispatch actions', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(dispatch.mock.calls.length).toBe(2); - }); - }); - - test('calls QUERY_EDITOR_SAVED after making a request', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(dispatch.mock.calls[0][0].type).toBe(actions.QUERY_EDITOR_SAVED); - }); - }); - - test('onSave calls QUERY_EDITOR_SAVED and QUERY_EDITOR_SET_TITLE', () => { - expect.assertions(1); - - const store = mockStore(initialState); - const expectedActionTypes = [ - actions.QUERY_EDITOR_SAVED, - actions.QUERY_EDITOR_SET_TITLE, - ]; - return store.dispatch(actions.saveQuery(query, queryId)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('formatQuery', () => { - const formatQueryEndpoint = 'glob:*/api/v1/sqllab/format_sql/'; - const expectedSql = 'SELECT 1'; - - beforeEach(() => { - fetchMock.removeRoute(formatQueryEndpoint); - fetchMock.post( - formatQueryEndpoint, - { result: expectedSql }, - { - name: formatQueryEndpoint, - }, - ); - }); - - test('posts to the correct url', async () => { - const store = mockStore(initialState); - store.dispatch(actions.formatQuery(query as unknown as QueryEditor)); - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - expect(store.getActions()[0].type).toBe(actions.QUERY_EDITOR_SET_SQL); - expect(store.getActions()[0].sql).toBe(expectedSql); - }); - - test('sends only sql in request body when no dbId or templateParams', async () => { - const queryEditorWithoutExtras = { - ...defaultQueryEditor, - sql: 'SELECT * FROM table', - dbId: null, - templateParams: null, - }; - const state = { - sqlLab: { - queryEditors: [queryEditorWithoutExtras], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - store.dispatch( - actions.formatQuery(queryEditorWithoutExtras as unknown as QueryEditor), - ); - - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - - const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - - expect(body).toEqual({ sql: 'SELECT * FROM table' }); - expect(body.database_id).toBeUndefined(); - expect(body.template_params).toBeUndefined(); - }); - - test('includes database_id in request when dbId is provided', async () => { - const queryEditorWithDb = { - ...defaultQueryEditor, - sql: 'SELECT * FROM table', - dbId: 5, - templateParams: null, - }; - const state = { - sqlLab: { - queryEditors: [queryEditorWithDb], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - store.dispatch( - actions.formatQuery(queryEditorWithDb as unknown as QueryEditor), - ); - - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - - const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - - expect(body).toEqual({ - sql: 'SELECT * FROM table', - database_id: 5, - }); - }); - - test('includes template_params as string when provided as string', async () => { - const queryEditorWithTemplateString = { - ...defaultQueryEditor, - sql: 'SELECT * FROM table WHERE id = {{ user_id }}', - dbId: 5, - templateParams: '{"user_id": 123}', - }; - const state = { - sqlLab: { - queryEditors: [queryEditorWithTemplateString], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - store.dispatch( - actions.formatQuery( - queryEditorWithTemplateString as unknown as QueryEditor, - ), - ); - - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - - const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - - expect(body).toEqual({ - sql: 'SELECT * FROM table WHERE id = {{ user_id }}', - database_id: 5, - template_params: '{"user_id": 123}', - }); - }); - - test('stringifies template_params when provided as object', async () => { - const queryEditorWithTemplateObject = { - ...defaultQueryEditor, - sql: 'SELECT * FROM table WHERE id = {{ user_id }}', - dbId: 5, - templateParams: { user_id: 123, status: 'active' }, - }; - const state = { - sqlLab: { - queryEditors: [queryEditorWithTemplateObject], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - store.dispatch( - actions.formatQuery( - queryEditorWithTemplateObject as unknown as QueryEditor, - ), - ); - - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - - const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - - expect(body).toEqual({ - sql: 'SELECT * FROM table WHERE id = {{ user_id }}', - database_id: 5, - template_params: '{"user_id":123,"status":"active"}', - }); - }); - - test('dispatches QUERY_EDITOR_SET_SQL with formatted result', async () => { - const formattedSql = 'SELECT\n *\nFROM\n table'; - fetchMock.removeRoute(formatQueryEndpoint); - fetchMock.route( - formatQueryEndpoint, - { result: formattedSql }, - { name: formatQueryEndpoint }, - ); - - const queryEditorToFormat = { - ...defaultQueryEditor, - sql: 'SELECT * FROM table', - }; - const state = { - sqlLab: { - queryEditors: [queryEditorToFormat], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - await store.dispatch(actions.formatQuery(queryEditorToFormat)); - - const dispatchedActions = store.getActions(); - expect(dispatchedActions).toHaveLength(1); - expect(dispatchedActions[0].type).toBe(actions.QUERY_EDITOR_SET_SQL); - expect(dispatchedActions[0].sql).toBe(formattedSql); - }); - - test('uses up-to-date query editor state from store', async () => { - const outdatedQueryEditor = { - ...defaultQueryEditor, - sql: 'OLD SQL', - dbId: 1, - }; - const upToDateQueryEditor = { - ...defaultQueryEditor, - sql: 'SELECT * FROM updated_table', - dbId: 10, - }; - const state = { - sqlLab: { - queryEditors: [upToDateQueryEditor], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - - // Pass outdated query editor, but expect the function to use the up-to-date one from store - store.dispatch(actions.formatQuery(outdatedQueryEditor)); - - await waitFor(() => - expect(fetchMock.callHistory.calls(formatQueryEndpoint)).toHaveLength( - 1, - ), - ); - - const call = fetchMock.callHistory.calls(formatQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - - expect(body.sql).toBe('SELECT * FROM updated_table'); - expect(body.database_id).toBe(10); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('fetchQueryResults', () => { - const makeRequest = () => { - const store = mockStore(initialState); - const request = actions.fetchQueryResults(query); - return request(dispatch, store.getState, undefined); - }; - - test('makes the fetch request', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(fetchMock.callHistory.calls(fetchQueryEndpoint)).toHaveLength(1); - }); - }); - - test('calls requestQueryResults', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(dispatch.mock.calls[0][0].type).toBe( - actions.REQUEST_QUERY_RESULTS, - ); - }); - }); - - /* oxlint-disable-next-line jest/no-disabled-tests */ - test.skip('parses large number result without losing precision', () => - makeRequest().then(() => { - expect(fetchMock.callHistory.calls(fetchQueryEndpoint)).toHaveLength(1); - expect(dispatch.mock.calls.length).toBe(2); - expect( - dispatch.mock.calls[1][ - dispatch.mock.calls[1].length - 1 - ].results.data.toString(), - ).toBe(mockBigNumber); - })); - - test('calls querySuccess on fetch success', () => { - expect.assertions(1); - - const store = mockStore({}); - const expectedActionTypes = [ - actions.REQUEST_QUERY_RESULTS, - actions.QUERY_SUCCESS, - ]; - return store.dispatch(actions.fetchQueryResults(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - }); - }); - - test('calls queryFailed on fetch error', () => { - expect.assertions(1); - - fetchMock.removeRoute(fetchQueryEndpoint); - fetchMock.get( - fetchQueryEndpoint, - { throws: { message: 'error text' } }, - { name: fetchQueryEndpoint }, - ); - - const store = mockStore({}); - const expectedActionTypes = [ - actions.REQUEST_QUERY_RESULTS, - actions.QUERY_FAILED, - ]; - return store.dispatch(actions.fetchQueryResults(query)).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('runQuery without query params', () => { - const makeRequest = () => { - const request = actions.runQuery(query); - return request(dispatch, () => typedInitialState, undefined); - }; - - test('makes the fetch request', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); - }); - }); - - test('calls startQuery', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(dispatch.mock.calls[0][0].type).toBe(actions.START_QUERY); - }); - }); - - test('parses large number result without losing precision', () => - makeRequest().then(() => { - expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); - expect(dispatch.mock.calls.length).toBe(2); - expect( - dispatch.mock.calls[1][ - dispatch.mock.calls[1].length - 1 - ].results.data.toString(), - ).toBe(mockBigNumber); - })); - - test('calls querySuccess on fetch success', () => { - expect.assertions(1); - - const store = mockStore({}); - const expectedActionTypes = [actions.START_QUERY, actions.QUERY_SUCCESS]; - const { dispatch } = store; - const request = actions.runQuery(query); - return request(dispatch, () => typedInitialState, undefined).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - }); - }); - - test('calls queryFailed on fetch error and logs the error details', () => { - expect.assertions(2); - - fetchMock.removeRoute(runQueryEndpoint); - fetchMock.post( - runQueryEndpoint, - { - throws: { - message: 'error text', - timeout: true, - statusText: 'timeout', - }, - }, - { name: runQueryEndpoint }, - ); - - const store = mockStore({}); - const expectedActionTypes = [ - actions.START_QUERY, - LOG_EVENT, - actions.QUERY_FAILED, - ]; - const { dispatch } = store; - const request = actions.runQuery(query); - return request(dispatch, () => typedInitialState, undefined).then(() => { - const actions = store.getActions(); - expect(actions.map(a => a.type)).toEqual(expectedActionTypes); - expect(actions[1].payload.eventData.issue_codes).toEqual([1000, 1001]); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('runQuery with query params', () => { - const { location } = window; - - beforeAll(() => { - delete (window as any).location; - (window as any).location = new URL('http://localhost/sqllab/?foo=bar'); - }); - - afterAll(() => { - delete (window as any).location; - window.location = location; - }); - - const makeRequest = () => { - const request = actions.runQuery(query); - return request(dispatch, () => typedInitialState, undefined); - }; - - test('makes the fetch request', async () => { - const runQueryEndpointWithParams = - 'glob:*/api/v1/sqllab/execute/?foo=bar'; - fetchMock.post( - runQueryEndpointWithParams, - `{ "data": ${mockBigNumber} }`, - ); - await makeRequest().then(() => { - expect( - fetchMock.callHistory.calls(runQueryEndpointWithParams), - ).toHaveLength(1); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('reRunQuery', () => { - test('creates new query with a new id', () => { - const id = 'id'; - const state = { - sqlLab: { - tabHistory: [id], - queryEditors: [{ id, name: 'Dummy query editor' }], - unsavedQueryEditor: {}, - }, - }; - const store = mockStore(state); - const request = actions.reRunQuery(query); - request(store.dispatch, store.getState, undefined); - expect(store.getActions()[0].query.id).toEqual('abcd'); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('postStopQuery', () => { - const stopQueryEndpoint = 'glob:*/api/v1/query/stop'; - fetchMock.post(stopQueryEndpoint, {}); - const baseQuery = { - ...query, - id: 'test_foo', - }; - - const makeRequest = () => { - const request = actions.postStopQuery(baseQuery); - return request(dispatch, () => typedInitialState, undefined); - }; - - test('makes the fetch request', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(fetchMock.callHistory.calls(stopQueryEndpoint)).toHaveLength(1); - }); - }); - - test('calls stopQuery', () => { - expect.assertions(1); - - return makeRequest().then(() => { - expect(dispatch.mock.calls[0][0].type).toBe(actions.STOP_QUERY); - }); - }); - - test('sends the correct data', () => { - expect.assertions(1); - - return makeRequest().then(() => { - const call = fetchMock.callHistory.calls(stopQueryEndpoint)[0]; - const body = JSON.parse(call.options.body as string); - expect(body.client_id).toBe(baseQuery.id); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('cloneQueryToNewTab', () => { - test('creates new query editor', () => { - expect.assertions(1); - - const id = 'id'; - const state = { - sqlLab: { - tabHistory: [id], - queryEditors: [{ id, name: 'out of updated title' }], - unsavedQueryEditor: { - id, - name: 'Dummy query editor', - }, - }, - }; - const store = mockStore(state); - const expectedActions = [ - { - type: actions.ADD_QUERY_EDITOR, - queryEditor: { - name: 'Copy of Dummy query editor', - dbId: 1, - catalog: query.catalog, - schema: query.schema, - autorun: true, - sql: 'SELECT * FROM something', - queryLimit: undefined, - maxRow: undefined, - id: 'abcd', - immutableId: 'abcd', - templateParams: undefined, - inLocalStorage: true, - loaded: true, - }, - }, - ]; - const request = actions.cloneQueryToNewTab(query, true); - request(store.dispatch, store.getState, undefined); - - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('popSavedQuery', () => { - const supersetClientGetSpy = jest.spyOn(SupersetClient, 'get'); const store = mockStore({}); + const expectedActionTypes = [ + actions.REQUEST_QUERY_RESULTS, + actions.QUERY_FAILED, + ]; + return store.dispatch(actions.fetchQueryResults(query)).then(() => { + expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + }); + }); +}); - const mockSavedQueryApiResponse = { - catalog: null, - changed_by: { - first_name: 'Superset', - id: 1, - last_name: 'Admin', +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('runQuery without query params', () => { + const makeRequest = () => { + const request = actions.runQuery(query); + return request(dispatch, () => typedInitialState, undefined); + }; + + test('makes the fetch request', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); + }); + }); + + test('calls startQuery', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(dispatch.mock.calls[0][0].type).toBe(actions.START_QUERY); + }); + }); + + test('parses large number result without losing precision', () => + makeRequest().then(() => { + expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); + expect(dispatch.mock.calls.length).toBe(2); + expect( + dispatch.mock.calls[1][ + dispatch.mock.calls[1].length - 1 + ].results.data.toString(), + ).toBe(mockBigNumber); + })); + + test('calls querySuccess on fetch success', () => { + expect.assertions(1); + + const store = mockStore({}); + const expectedActionTypes = [actions.START_QUERY, actions.QUERY_SUCCESS]; + const { dispatch } = store; + const request = actions.runQuery(query); + return request(dispatch, () => typedInitialState, undefined).then(() => { + expect(store.getActions().map(a => a.type)).toEqual(expectedActionTypes); + }); + }); + + test('calls queryFailed on fetch error and logs the error details', ({ + expect, + }) => { + expect.assertions(2); + + fetchMock.removeRoute(runQueryEndpoint); + fetchMock.post( + runQueryEndpoint, + { + throws: { + message: 'error text', + timeout: true, + statusText: 'timeout', + }, }, - changed_on: '2024-12-28T20:06:14.246743', - changed_on_delta_humanized: '8 days ago', - created_by: { - first_name: 'Superset', - id: 1, - last_name: 'Admin', + { name: runQueryEndpoint }, + ); + + const store = mockStore({}); + const expectedActionTypes = [ + actions.START_QUERY, + LOG_EVENT, + actions.QUERY_FAILED, + ]; + const { dispatch } = store; + const request = actions.runQuery(query); + return request(dispatch, () => typedInitialState, undefined).then(() => { + const actions = store.getActions(); + expect(actions.map(a => a.type)).toEqual(expectedActionTypes); + expect(actions[1].payload.eventData.issue_codes).toEqual([1000, 1001]); + }); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('runQuery with query params', () => { + const { location } = window; + + beforeAll(() => { + delete (window as any).location; + (window as any).location = new URL('http://localhost/sqllab/?foo=bar'); + }); + + afterAll(() => { + delete (window as any).location; + // @ts-ignore + window.location = location; + }); + + const makeRequest = () => { + const request = actions.runQuery(query); + return request(dispatch, () => typedInitialState, undefined); + }; + + test('makes the fetch request', async () => { + const runQueryEndpointWithParams = 'glob:*/api/v1/sqllab/execute/?foo=bar'; + fetchMock.post(runQueryEndpointWithParams, `{ "data": ${mockBigNumber} }`); + await makeRequest().then(() => { + expect( + fetchMock.callHistory.calls(runQueryEndpointWithParams), + ).toHaveLength(1); + }); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('reRunQuery', () => { + test('creates new query with a new id', () => { + const id = 'id'; + const state = { + sqlLab: { + tabHistory: [id], + queryEditors: [{ id, name: 'Dummy query editor' }], + unsavedQueryEditor: {}, }, - database: { - database_name: 'examples', - id: 2, + }; + const store = mockStore(state); + const request = actions.reRunQuery(query); + request(store.dispatch, store.getState, undefined); + expect(store.getActions()[0].query.id).toEqual('abcd'); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('postStopQuery', () => { + const stopQueryEndpoint = 'glob:*/api/v1/query/stop'; + fetchMock.post(stopQueryEndpoint, {}); + const baseQuery = { + ...query, + id: 'test_foo', + }; + + const makeRequest = () => { + const request = actions.postStopQuery(baseQuery); + return request(dispatch, () => typedInitialState, undefined); + }; + + test('makes the fetch request', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(fetchMock.callHistory.calls(stopQueryEndpoint)).toHaveLength(1); + }); + }); + + test('calls stopQuery', () => { + expect.assertions(1); + + return makeRequest().then(() => { + expect(dispatch.mock.calls[0][0].type).toBe(actions.STOP_QUERY); + }); + }); + + test('sends the correct data', () => { + expect.assertions(1); + + return makeRequest().then(() => { + const call = fetchMock.callHistory.calls(stopQueryEndpoint)[0]; + const body = JSON.parse(call.options.body as string); + expect(body.client_id).toBe(baseQuery.id); + }); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('cloneQueryToNewTab', () => { + test('creates new query editor', () => { + expect.assertions(1); + + const id = 'id'; + const state = { + sqlLab: { + tabHistory: [id], + queryEditors: [{ id, name: 'out of updated title' }], + unsavedQueryEditor: { + id, + name: 'Dummy query editor', + }, }, - description: '', + }; + const store = mockStore(state); + const expectedActions = [ + { + type: actions.ADD_QUERY_EDITOR, + queryEditor: { + name: 'Copy of Dummy query editor', + dbId: 1, + catalog: query.catalog, + schema: query.schema, + autorun: true, + sql: 'SELECT * FROM something', + queryLimit: undefined, + maxRow: undefined, + id: 'abcd', + immutableId: 'abcd', + templateParams: undefined, + inLocalStorage: true, + loaded: true, + }, + }, + ]; + const request = actions.cloneQueryToNewTab(query, true); + request(store.dispatch, store.getState, undefined); + + expect(store.getActions()).toEqual(expectedActions); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('popSavedQuery', () => { + const supersetClientGetSpy = vi.spyOn(SupersetClient, 'get'); + const store = mockStore({}); + + const mockSavedQueryApiResponse = { + catalog: null, + changed_by: { + first_name: 'Superset', id: 1, - label: 'Query 1', + last_name: 'Admin', + }, + changed_on: '2024-12-28T20:06:14.246743', + changed_on_delta_humanized: '8 days ago', + created_by: { + first_name: 'Superset', + id: 1, + last_name: 'Admin', + }, + database: { + database_name: 'examples', + id: 2, + }, + description: '', + id: 1, + label: 'Query 1', + schema: 'public', + sql: 'SELECT * FROM channels', + sql_tables: [ + { + catalog: null, + schema: null, + table: 'channels', + }, + ], + template_parameters: null, + }; + + const makeRequest = (id: string | number) => { + const request = actions.popSavedQuery(String(id)); + const { dispatch } = store; + + return request(dispatch, () => typedInitialState, undefined); + }; + + beforeEach(() => { + supersetClientGetSpy.mockClear(); + store.clearActions(); + }); + + afterAll(() => { + supersetClientGetSpy.mockRestore(); + }); + + test('calls API endpint with correct params', async () => { + supersetClientGetSpy.mockResolvedValue({ + json: { result: mockSavedQueryApiResponse }, + } as any); + + await makeRequest(123); + + expect(supersetClientGetSpy).toHaveBeenCalledWith({ + endpoint: '/api/v1/saved_query/123', + }); + }); + + test('dispatches addQueryEditor with correct params on successful API call', async () => { + supersetClientGetSpy.mockResolvedValue({ + json: { result: mockSavedQueryApiResponse }, + } as any); + + const expectedParams = { + name: 'Query 1', + dbId: 2, + catalog: null, schema: 'public', sql: 'SELECT * FROM channels', - sql_tables: [ - { - catalog: null, - schema: null, - table: 'channels', - }, - ], - template_parameters: null, + templateParams: null, + remoteId: 1, }; - const makeRequest = (id: string | number) => { - const request = actions.popSavedQuery(String(id)); - const { dispatch } = store; + await makeRequest(1); - return request(dispatch, () => typedInitialState, undefined); - }; + const addQueryEditorAction = store + .getActions() + .find(action => action.type === actions.ADD_QUERY_EDITOR); - beforeEach(() => { - supersetClientGetSpy.mockClear(); - store.clearActions(); - }); - - afterAll(() => { - supersetClientGetSpy.mockRestore(); - }); - - test('calls API endpint with correct params', async () => { - supersetClientGetSpy.mockResolvedValue({ - json: { result: mockSavedQueryApiResponse }, - } as any); - - await makeRequest(123); - - expect(supersetClientGetSpy).toHaveBeenCalledWith({ - endpoint: '/api/v1/saved_query/123', - }); - }); - - test('dispatches addQueryEditor with correct params on successful API call', async () => { - supersetClientGetSpy.mockResolvedValue({ - json: { result: mockSavedQueryApiResponse }, - } as any); - - const expectedParams = { - name: 'Query 1', - dbId: 2, - catalog: null, - schema: 'public', - sql: 'SELECT * FROM channels', - templateParams: null, - remoteId: 1, - }; - - await makeRequest(1); - - const addQueryEditorAction = store - .getActions() - .find(action => action.type === actions.ADD_QUERY_EDITOR); - - expect(addQueryEditorAction).toBeTruthy(); - expect(addQueryEditorAction?.queryEditor).toEqual( - expect.objectContaining(expectedParams), - ); - }); - - test('should dispatch addDangerToast on API error', async () => { - supersetClientGetSpy.mockResolvedValue(new Error() as any); - - await makeRequest(1); - - const addToastAction = store - .getActions() - .find(action => action.type === ADD_TOAST); - - expect(addToastAction).toBeTruthy(); - expect(addToastAction?.payload?.toastType).toBe(ToastType.Danger); - }); + expect(addQueryEditorAction).toBeTruthy(); + expect(addQueryEditorAction?.queryEditor).toEqual( + expect.objectContaining(expectedParams), + ); }); - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('addQueryEditor', () => { - test('creates new query editor', () => { - expect.assertions(1); + test('should dispatch addDangerToast on API error', async () => { + supersetClientGetSpy.mockResolvedValue(new Error() as any); - const store = mockStore(initialState); - const expectedActions = [ - { - type: actions.ADD_QUERY_EDITOR, - queryEditor: { - ...queryEditor, - immutableId: 'abcd', - inLocalStorage: true, - loaded: true, - }, - }, - ]; - store.dispatch(actions.addQueryEditor(defaultQueryEditor)); + await makeRequest(1); - expect(store.getActions()).toEqual(expectedActions); - }); + const addToastAction = store + .getActions() + .find(action => action.type === ADD_TOAST); - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('addNewQueryEditor', () => { - test('creates new query editor with new tab name', () => { - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - unsavedQueryEditor: { - id: defaultQueryEditor.id, - name: 'Untitled Query 6', - }, - }, - }); - const expectedActions = [ - { - type: actions.ADD_QUERY_EDITOR, - queryEditor: { - id: 'abcd', - immutableId: 'abcd', - sql: expect.stringContaining('SELECT ...'), - name: `Untitled Query 7`, - dbId: defaultQueryEditor.dbId, - catalog: defaultQueryEditor.catalog, - schema: defaultQueryEditor.schema, - autorun: false, - queryLimit: - (defaultQueryEditor as any).queryLimit || - initialState.common.conf.DEFAULT_SQLLAB_LIMIT, - inLocalStorage: true, - loaded: true, - }, - }, - ]; - const request = actions.addNewQueryEditor(); - request(store.dispatch, store.getState, undefined); - expect(store.getActions()).toEqual(expectedActions); - }); - }); + expect(addToastAction).toBeTruthy(); + expect(addToastAction?.payload?.toastType).toBe(ToastType.Danger); }); +}); - test('set current query editor', () => { +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('addQueryEditor', () => { + test('creates new query editor', () => { expect.assertions(1); const store = mockStore(initialState); const expectedActions = [ { - type: actions.SET_ACTIVE_QUERY_EDITOR, - queryEditor: defaultQueryEditor, + type: actions.ADD_QUERY_EDITOR, + queryEditor: { + ...queryEditor, + immutableId: 'abcd', + inLocalStorage: true, + loaded: true, + }, }, ]; - store.dispatch(actions.setActiveQueryEditor(defaultQueryEditor)); + store.dispatch(actions.addQueryEditor(defaultQueryEditor)); expect(store.getActions()).toEqual(expectedActions); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('swithQueryEditor', () => { - test('switch to the next tab editor', () => { - const store = mockStore(initialState); - const expectedActions = [ - { - type: actions.SET_ACTIVE_QUERY_EDITOR, - queryEditor: initialState.sqlLab.queryEditors[1], - }, - ]; - store.dispatch(actions.switchQueryEditor()); - - expect(store.getActions()).toEqual(expectedActions); - }); - - test('switch to the first tab editor once it reaches the rightmost tab', () => { + describe('addNewQueryEditor', () => { + test('creates new query editor with new tab name', () => { const store = mockStore({ ...initialState, sqlLab: { ...initialState.sqlLab, - tabHistory: [ - initialState.sqlLab.queryEditors[ - initialState.sqlLab.queryEditors.length - 1 - ].id, + unsavedQueryEditor: { + id: defaultQueryEditor.id, + name: 'Untitled Query 6', + }, + }, + }); + const expectedActions = [ + { + type: actions.ADD_QUERY_EDITOR, + queryEditor: { + id: 'abcd', + immutableId: 'abcd', + sql: expect.stringContaining('SELECT ...'), + name: `Untitled Query 7`, + dbId: defaultQueryEditor.dbId, + catalog: defaultQueryEditor.catalog, + schema: defaultQueryEditor.schema, + autorun: false, + queryLimit: + (defaultQueryEditor as any).queryLimit || + initialState.common.conf.DEFAULT_SQLLAB_LIMIT, + inLocalStorage: true, + loaded: true, + }, + }, + ]; + const request = actions.addNewQueryEditor(); + request(store.dispatch, store.getState, undefined); + expect(store.getActions()).toEqual(expectedActions); + }); + }); +}); + +test('set current query editor', () => { + expect.assertions(1); + + const store = mockStore(initialState); + const expectedActions = [ + { + type: actions.SET_ACTIVE_QUERY_EDITOR, + queryEditor: defaultQueryEditor, + }, + ]; + store.dispatch(actions.setActiveQueryEditor(defaultQueryEditor)); + + expect(store.getActions()).toEqual(expectedActions); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('swithQueryEditor', () => { + test('switch to the next tab editor', () => { + const store = mockStore(initialState); + const expectedActions = [ + { + type: actions.SET_ACTIVE_QUERY_EDITOR, + queryEditor: initialState.sqlLab.queryEditors[1], + }, + ]; + store.dispatch(actions.switchQueryEditor()); + + expect(store.getActions()).toEqual(expectedActions); + }); + + test('switch to the first tab editor once it reaches the rightmost tab', () => { + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + tabHistory: [ + initialState.sqlLab.queryEditors[ + initialState.sqlLab.queryEditors.length - 1 + ].id, + ], + }, + }); + const expectedActions = [ + { + type: actions.SET_ACTIVE_QUERY_EDITOR, + queryEditor: initialState.sqlLab.queryEditors[0], + }, + ]; + store.dispatch(actions.switchQueryEditor()); + + expect(store.getActions()).toEqual(expectedActions); + }); + + test('switch to the previous tab editor', () => { + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + tabHistory: [initialState.sqlLab.queryEditors[1].id], + }, + }); + const expectedActions = [ + { + type: actions.SET_ACTIVE_QUERY_EDITOR, + queryEditor: initialState.sqlLab.queryEditors[0], + }, + ]; + store.dispatch(actions.switchQueryEditor(true)); + + expect(store.getActions()).toEqual(expectedActions); + }); + + test('switch to the last tab editor once it reaches the leftmost tab', () => { + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + tabHistory: [initialState.sqlLab.queryEditors[0].id], + }, + }); + const expectedActions = [ + { + type: actions.SET_ACTIVE_QUERY_EDITOR, + queryEditor: + initialState.sqlLab.queryEditors[ + initialState.sqlLab.queryEditors.length - 1 ], - }, - }); + }, + ]; + store.dispatch(actions.switchQueryEditor(true)); + + expect(store.getActions()).toEqual(expectedActions); + }); +}); + +// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks +describe('backend sync', () => { + const updateTabStateEndpoint = 'glob:*/tabstateview/*'; + fetchMock.put(updateTabStateEndpoint, {}); + fetchMock.delete(updateTabStateEndpoint, {}); + fetchMock.post(updateTabStateEndpoint, JSON.stringify({ id: 1 })); + + const updateTableSchemaEndpoint = 'glob:*/tableschemaview/*'; + fetchMock.put(updateTableSchemaEndpoint, {}); + fetchMock.delete(updateTableSchemaEndpoint, {}); + fetchMock.post(updateTableSchemaEndpoint, JSON.stringify({ id: 1 })); + + const updateTableSchemaExpandedEndpoint = + 'glob:**/tableschemaview/*/expanded'; + fetchMock.post(updateTableSchemaExpandedEndpoint, {}); + + const getTableMetadataEndpoint = 'glob:**/api/v1/database/*/table_metadata/*'; + fetchMock.get(getTableMetadataEndpoint, {}); + const getExtraTableMetadataEndpoint = + 'glob:**/api/v1/database/*/table_metadata/extra/*'; + fetchMock.get(getExtraTableMetadataEndpoint, {}); + + beforeEach(() => { + isFeatureEnabledMock.mockImplementation( + (feature: string) => feature === 'SQLLAB_BACKEND_PERSISTENCE', + ); + }); + + afterEach(() => { + isFeatureEnabledMock.mockRestore(); + }); + + afterEach(() => fetchMock.clearHistory()); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('addQueryEditor', () => { + test('creates the tab state in the local storage', () => { + expect.assertions(2); + + const store = mockStore({}); const expectedActions = [ { - type: actions.SET_ACTIVE_QUERY_EDITOR, - queryEditor: initialState.sqlLab.queryEditors[0], + type: actions.ADD_QUERY_EDITOR, + queryEditor: { + ...queryEditor, + id: 'abcd', + immutableId: 'abcd', + loaded: true, + inLocalStorage: true, + }, }, ]; - store.dispatch(actions.switchQueryEditor()); + store.dispatch(actions.addQueryEditor(queryEditor)); expect(store.getActions()).toEqual(expectedActions); + expect(fetchMock.callHistory.calls(updateTabStateEndpoint)).toHaveLength( + 0, + ); }); + }); - test('switch to the previous tab editor', () => { - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - tabHistory: [initialState.sqlLab.queryEditors[1].id], - }, - }); + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('removeQueryEditor', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const store = mockStore({}); const expectedActions = [ { - type: actions.SET_ACTIVE_QUERY_EDITOR, - queryEditor: initialState.sqlLab.queryEditors[0], + type: actions.REMOVE_QUERY_EDITOR, + queryEditor, }, ]; - store.dispatch(actions.switchQueryEditor(true)); - + store.dispatch(actions.removeQueryEditor(queryEditor)); expect(store.getActions()).toEqual(expectedActions); }); + }); - test('switch to the last tab editor once it reaches the leftmost tab', () => { - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - tabHistory: [initialState.sqlLab.queryEditors[0].id], - }, - }); + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetDb', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const dbId = 42; + const store = mockStore({}); const expectedActions = [ { - type: actions.SET_ACTIVE_QUERY_EDITOR, - queryEditor: - initialState.sqlLab.queryEditors[ - initialState.sqlLab.queryEditors.length - 1 - ], + type: actions.QUERY_EDITOR_SETDB, + queryEditor, + dbId, }, ]; - store.dispatch(actions.switchQueryEditor(true)); + store.dispatch(actions.queryEditorSetDb(queryEditor, dbId)); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetCatalog', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const catalog = 'public'; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_CATALOG, + queryEditor, + catalog, + }, + ]; + store.dispatch(actions.queryEditorSetCatalog(queryEditor, catalog)); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetSchema', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const schema = 'schema'; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_SCHEMA, + queryEditor, + schema, + }, + ]; + store.dispatch(actions.queryEditorSetSchema(queryEditor, schema)); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetAutorun', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const autorun = true; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_AUTORUN, + queryEditor, + autorun, + }, + ]; + store.dispatch(actions.queryEditorSetAutorun(queryEditor, autorun)); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetTitle', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const name = 'name'; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_TITLE, + queryEditor, + name, + }, + ]; + store.dispatch( + actions.queryEditorSetTitle(queryEditor, name, queryEditor.id), + ); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetAndSaveSql', () => { + const sql = 'SELECT * '; + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_SQL, + queryEditor, + sql, + }, + ]; + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('with backend persistence flag on', () => { + test('updates the tab state in the backend', () => { + expect.assertions(2); + + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + queryEditors: [queryEditor], + }, + }); + const request = actions.queryEditorSetAndSaveSql(queryEditor, sql); + return request(store.dispatch, store.getState, undefined).then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTabStateEndpoint), + ).toHaveLength(1); + }); + }); + }); + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('with backend persistence flag off', () => { + test('does not update the tab state in the backend', () => { + isFeatureEnabledMock.mockImplementation( + (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), + ); + + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + queryEditors: [queryEditor], + }, + }); + const request = actions.queryEditorSetAndSaveSql(queryEditor, sql); + request(store.dispatch, store.getState, undefined); + + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTabStateEndpoint), + ).toHaveLength(0); + isFeatureEnabledMock.mockRestore(); + }); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetQueryLimit', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const queryLimit = 10; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_QUERY_LIMIT, + queryEditor, + queryLimit, + }, + ]; + store.dispatch(actions.queryEditorSetQueryLimit(queryEditor, queryLimit)); + expect(store.getActions()).toEqual(expectedActions); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('queryEditorSetTemplateParams', () => { + test('updates the tab state in the backend', () => { + expect.assertions(1); + + const templateParams = '{"foo": "bar"}'; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.QUERY_EDITOR_SET_TEMPLATE_PARAMS, + queryEditor, + templateParams, + }, + ]; + store.dispatch( + actions.queryEditorSetTemplateParams(queryEditor, templateParams), + ); expect(store.getActions()).toEqual(expectedActions); }); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('backend sync', () => { - const updateTabStateEndpoint = 'glob:*/tabstateview/*'; - fetchMock.put(updateTabStateEndpoint, {}); - fetchMock.delete(updateTabStateEndpoint, {}); - fetchMock.post(updateTabStateEndpoint, JSON.stringify({ id: 1 })); - - const updateTableSchemaEndpoint = 'glob:*/tableschemaview/*'; - fetchMock.put(updateTableSchemaEndpoint, {}); - fetchMock.delete(updateTableSchemaEndpoint, {}); - fetchMock.post(updateTableSchemaEndpoint, JSON.stringify({ id: 1 })); - - const updateTableSchemaExpandedEndpoint = - 'glob:**/tableschemaview/*/expanded'; - fetchMock.post(updateTableSchemaExpandedEndpoint, {}); - - const getTableMetadataEndpoint = - 'glob:**/api/v1/database/*/table_metadata/*'; - fetchMock.get(getTableMetadataEndpoint, {}); - const getExtraTableMetadataEndpoint = - 'glob:**/api/v1/database/*/table_metadata/extra/*'; - fetchMock.get(getExtraTableMetadataEndpoint, {}); - - beforeEach(() => { - isFeatureEnabledMock.mockImplementation( - (feature: string) => feature === 'SQLLAB_BACKEND_PERSISTENCE', + describe('addTable', () => { + test('dispatches table state from unsaved change', () => { + const tableName = 'table'; + const catalogName = null; + const schemaName = 'schema'; + const expectedDbId = 473892; + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + unsavedQueryEditor: { + id: query.id, + dbId: expectedDbId, + }, + }, + }); + const request = actions.addTable( + query, + tableName, + catalogName, + schemaName, + ); + request(store.dispatch, store.getState, undefined); + expect(store.getActions()[0]).toEqual( + expect.objectContaining({ + table: expect.objectContaining({ + name: tableName, + catalog: catalogName, + schema: schemaName, + dbId: expectedDbId, + }), + }), ); }); - afterEach(() => { - isFeatureEnabledMock.mockRestore(); + test('uses tabViewId when available', () => { + const tableName = 'table'; + const catalogName = null; + const schemaName = 'schema'; + const expectedDbId = 473892; + const tabViewId = '123'; + const queryWithTabViewId = { ...query, tabViewId }; + + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + unsavedQueryEditor: { + id: query.id, + dbId: expectedDbId, + }, + }, + }); + + const request = actions.addTable( + queryWithTabViewId, + tableName, + catalogName, + schemaName, + ); + request(store.dispatch, store.getState, undefined); + + expect(store.getActions()[0]).toEqual( + expect.objectContaining({ + table: expect.objectContaining({ + name: tableName, + catalog: catalogName, + schema: schemaName, + dbId: expectedDbId, + queryEditorId: tabViewId, // Should use tabViewId, not id + }), + }), + ); }); - afterEach(() => fetchMock.clearHistory()); + test('falls back to id when tabViewId is not available', () => { + const tableName = 'table'; + const catalogName = null; + const schemaName = 'schema'; + const expectedDbId = 473892; + const queryWithoutTabViewId = { ...query, tabViewId: undefined }; - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('addQueryEditor', () => { - test('creates the tab state in the local storage', () => { - expect.assertions(2); - - const store = mockStore({}); - const expectedActions = [ - { - type: actions.ADD_QUERY_EDITOR, - queryEditor: { - ...queryEditor, - id: 'abcd', - immutableId: 'abcd', - loaded: true, - inLocalStorage: true, - }, + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + unsavedQueryEditor: { + id: query.id, + dbId: expectedDbId, }, - ]; - store.dispatch(actions.addQueryEditor(queryEditor)); + }, + }); - expect(store.getActions()).toEqual(expectedActions); + const request = actions.addTable( + queryWithoutTabViewId, + tableName, + catalogName, + schemaName, + ); + request(store.dispatch, store.getState, undefined); + + expect(store.getActions()[0]).toEqual( + expect.objectContaining({ + table: expect.objectContaining({ + name: tableName, + catalog: catalogName, + schema: schemaName, + dbId: expectedDbId, + queryEditorId: query.id, // Should use id when tabViewId is not available + }), + }), + ); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('syncTable', () => { + test('updates the table schema state in the backend', () => { + expect.assertions(4); + + const tableName = 'table'; + const schemaName = 'schema'; + const store = mockStore(initialState); + const expectedActionTypes = [ + actions.MERGE_TABLE, // syncTable + ]; + const request = actions.syncTable( + query as any, + tableName as any, + schemaName, + ); + return request(store.dispatch, store.getState, undefined).then(() => { + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); + expect(store.getActions()[0].prepend).toBeFalsy(); + expect( + fetchMock.callHistory.calls(updateTableSchemaEndpoint), + ).toHaveLength(1); + + // tab state is not updated, since no query was run + expect( + fetchMock.callHistory.calls(updateTabStateEndpoint), + ).toHaveLength(0); + }); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('runTablePreviewQuery', () => { + const results = { + data: mockBigNumber, + query: { sqlEditorId: 'null', dbId: 1 }, + query_id: 'efgh', + }; + const tableName = 'table'; + const catalogName = null; + const schemaName = 'schema'; + const store = mockStore({ + ...initialState, + sqlLab: { + ...initialState.sqlLab, + databases: { + 1: { disable_data_preview: false }, + }, + }, + }); + + beforeEach(() => { + fetchMock.removeRoute(runQueryEndpoint); + fetchMock.post(runQueryEndpoint, JSON.stringify(results), { + name: runQueryEndpoint, + }); + }); + + afterEach(() => { + store.clearActions(); + fetchMock.clearHistory(); + }); + + test('updates and runs data preview query when configured', () => { + expect.assertions(3); + + const expectedActionTypes = [ + actions.MERGE_TABLE, // addTable (data preview) + actions.START_QUERY, // runQuery (data preview) + actions.QUERY_SUCCESS, // querySuccess + ]; + const request = actions.runTablePreviewQuery({ + dbId: 1, + name: tableName, + catalog: catalogName, + schema: schemaName, + }); + return request(store.dispatch, store.getState, undefined).then(() => { + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, + ); + expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); + // tab state is not updated, since the query is a data preview expect( fetchMock.callHistory.calls(updateTabStateEndpoint), ).toHaveLength(0); }); }); - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('removeQueryEditor', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const store = mockStore({}); - const expectedActions = [ - { - type: actions.REMOVE_QUERY_EDITOR, - queryEditor, - }, - ]; - store.dispatch(actions.removeQueryEditor(queryEditor)); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetDb', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const dbId = 42; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SETDB, - queryEditor, - dbId, - }, - ]; - store.dispatch(actions.queryEditorSetDb(queryEditor, dbId)); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetCatalog', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const catalog = 'public'; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_CATALOG, - queryEditor, - catalog, - }, - ]; - store.dispatch(actions.queryEditorSetCatalog(queryEditor, catalog)); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetSchema', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const schema = 'schema'; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_SCHEMA, - queryEditor, - schema, - }, - ]; - store.dispatch(actions.queryEditorSetSchema(queryEditor, schema)); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetAutorun', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const autorun = true; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_AUTORUN, - queryEditor, - autorun, - }, - ]; - store.dispatch(actions.queryEditorSetAutorun(queryEditor, autorun)); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetTitle', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const name = 'name'; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_TITLE, - queryEditor, - name, - }, - ]; - store.dispatch( - actions.queryEditorSetTitle(queryEditor, name, queryEditor.id), - ); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetAndSaveSql', () => { - const sql = 'SELECT * '; - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_SQL, - queryEditor, - sql, - }, + test('runs data preview query only', () => { + const expectedActionTypes = [ + actions.START_QUERY, // runQuery (data preview) + actions.QUERY_SUCCESS, // querySuccess ]; - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('with backend persistence flag on', () => { - test('updates the tab state in the backend', () => { - expect.assertions(2); - - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - queryEditors: [queryEditor], - }, - }); - const request = actions.queryEditorSetAndSaveSql(queryEditor, sql); - return request(store.dispatch, store.getState, undefined).then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(1); - }); - }); - }); - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('with backend persistence flag off', () => { - test('does not update the tab state in the backend', () => { - isFeatureEnabledMock.mockImplementation( - (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), - ); - - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - queryEditors: [queryEditor], - }, - }); - const request = actions.queryEditorSetAndSaveSql(queryEditor, sql); - request(store.dispatch, store.getState, undefined); - - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(0); - isFeatureEnabledMock.mockRestore(); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetQueryLimit', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const queryLimit = 10; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_QUERY_LIMIT, - queryEditor, - queryLimit, - }, - ]; - store.dispatch( - actions.queryEditorSetQueryLimit(queryEditor, queryLimit), - ); - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('queryEditorSetTemplateParams', () => { - test('updates the tab state in the backend', () => { - expect.assertions(1); - - const templateParams = '{"foo": "bar"}'; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.QUERY_EDITOR_SET_TEMPLATE_PARAMS, - queryEditor, - templateParams, - }, - ]; - store.dispatch( - actions.queryEditorSetTemplateParams(queryEditor, templateParams), - ); - - expect(store.getActions()).toEqual(expectedActions); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('addTable', () => { - test('dispatches table state from unsaved change', () => { - const tableName = 'table'; - const catalogName = null; - const schemaName = 'schema'; - const expectedDbId = 473892; - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - unsavedQueryEditor: { - id: query.id, - dbId: expectedDbId, - }, - }, - }); - const request = actions.addTable( - query, - tableName, - catalogName, - schemaName, - ); - request(store.dispatch, store.getState, undefined); - expect(store.getActions()[0]).toEqual( - expect.objectContaining({ - table: expect.objectContaining({ - name: tableName, - catalog: catalogName, - schema: schemaName, - dbId: expectedDbId, - }), - }), - ); - }); - - test('uses tabViewId when available', () => { - const tableName = 'table'; - const catalogName = null; - const schemaName = 'schema'; - const expectedDbId = 473892; - const tabViewId = '123'; - const queryWithTabViewId = { ...query, tabViewId }; - - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - unsavedQueryEditor: { - id: query.id, - dbId: expectedDbId, - }, - }, - }); - - const request = actions.addTable( - queryWithTabViewId, - tableName, - catalogName, - schemaName, - ); - request(store.dispatch, store.getState, undefined); - - expect(store.getActions()[0]).toEqual( - expect.objectContaining({ - table: expect.objectContaining({ - name: tableName, - catalog: catalogName, - schema: schemaName, - dbId: expectedDbId, - queryEditorId: tabViewId, // Should use tabViewId, not id - }), - }), - ); - }); - - test('falls back to id when tabViewId is not available', () => { - const tableName = 'table'; - const catalogName = null; - const schemaName = 'schema'; - const expectedDbId = 473892; - const queryWithoutTabViewId = { ...query, tabViewId: undefined }; - - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - unsavedQueryEditor: { - id: query.id, - dbId: expectedDbId, - }, - }, - }); - - const request = actions.addTable( - queryWithoutTabViewId, - tableName, - catalogName, - schemaName, - ); - request(store.dispatch, store.getState, undefined); - - expect(store.getActions()[0]).toEqual( - expect.objectContaining({ - table: expect.objectContaining({ - name: tableName, - catalog: catalogName, - schema: schemaName, - dbId: expectedDbId, - queryEditorId: query.id, // Should use id when tabViewId is not available - }), - }), - ); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('syncTable', () => { - test('updates the table schema state in the backend', () => { - expect.assertions(4); - - const tableName = 'table'; - const schemaName = 'schema'; - const store = mockStore(initialState); - const expectedActionTypes = [ - actions.MERGE_TABLE, // syncTable - ]; - const request = actions.syncTable( - query as any, - tableName as any, - schemaName, - ); - return request(store.dispatch, store.getState, undefined).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - expect(store.getActions()[0].prepend).toBeFalsy(); - expect( - fetchMock.callHistory.calls(updateTableSchemaEndpoint), - ).toHaveLength(1); - - // tab state is not updated, since no query was run - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(0); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('runTablePreviewQuery', () => { - const results = { - data: mockBigNumber, - query: { sqlEditorId: 'null', dbId: 1 }, - query_id: 'efgh', - }; - const tableName = 'table'; - const catalogName = null; - const schemaName = 'schema'; - const store = mockStore({ - ...initialState, - sqlLab: { - ...initialState.sqlLab, - databases: { - 1: { disable_data_preview: false }, - }, - }, - }); - - beforeEach(() => { - fetchMock.removeRoute(runQueryEndpoint); - fetchMock.post(runQueryEndpoint, JSON.stringify(results), { - name: runQueryEndpoint, - }); - }); - - afterEach(() => { - store.clearActions(); - fetchMock.clearHistory(); - }); - - test('updates and runs data preview query when configured', () => { - expect.assertions(3); - - const expectedActionTypes = [ - actions.MERGE_TABLE, // addTable (data preview) - actions.START_QUERY, // runQuery (data preview) - actions.QUERY_SUCCESS, // querySuccess - ]; - const request = actions.runTablePreviewQuery({ + const request = actions.runTablePreviewQuery( + { dbId: 1, name: tableName, catalog: catalogName, schema: schemaName, - }); - return request(store.dispatch, store.getState, undefined).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); - // tab state is not updated, since the query is a data preview - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(0); - }); - }); - - test('runs data preview query only', () => { - const expectedActionTypes = [ - actions.START_QUERY, // runQuery (data preview) - actions.QUERY_SUCCESS, // querySuccess - ]; - const request = actions.runTablePreviewQuery( - { - dbId: 1, - name: tableName, - catalog: catalogName, - schema: schemaName, - }, - true, + }, + true, + ); + return request(store.dispatch, store.getState, undefined).then(() => { + expect(store.getActions().map(a => a.type)).toEqual( + expectedActionTypes, ); - return request(store.dispatch, store.getState, undefined).then(() => { - expect(store.getActions().map(a => a.type)).toEqual( - expectedActionTypes, - ); - expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); - // tab state is not updated, since the query is a data preview - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(0); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('expandTable', () => { - test('updates the table schema state in the backend when initialized', () => { - expect.assertions(2); - - const table = { id: 1, initialized: true }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.EXPAND_TABLE, - table, - }, - ]; - return store - .dispatch(actions.expandTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(1); - }); - }); - - test('does not call backend when table is not initialized', () => { - expect.assertions(2); - - const table = { id: 'yVJPtuSackF', initialized: false }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.EXPAND_TABLE, - table, - }, - ]; - return store - .dispatch(actions.expandTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - // Check all POST calls to find the expanded endpoint - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - }); - }); - - test('does not call backend when initialized is undefined', () => { - expect.assertions(2); - - const table = { id: 'yVJPtuSackF' }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.EXPAND_TABLE, - table, - }, - ]; - return store - .dispatch(actions.expandTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - // Check all POST calls to find the expanded endpoint - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - }); - }); - - test('does not call backend when feature flag is off', () => { - expect.assertions(2); - - isFeatureEnabledMock.mockImplementation( - (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), - ); - - const table = { id: 1, initialized: true }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.EXPAND_TABLE, - table, - }, - ]; - return store - .dispatch(actions.expandTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - // Check all POST calls to find the expanded endpoint - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - isFeatureEnabledMock.mockRestore(); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('collapseTable', () => { - test('updates the table schema state in the backend when initialized', () => { - expect.assertions(2); - - const table = { id: 1, initialized: true }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.COLLAPSE_TABLE, - table, - }, - ]; - return store - .dispatch(actions.collapseTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(1); - }); - }); - - test('does not call backend when table is not initialized', () => { - expect.assertions(2); - - const table = { id: 'yVJPtuSackF', initialized: false }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.COLLAPSE_TABLE, - table, - }, - ]; - return store - .dispatch(actions.collapseTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - }); - }); - - test('does not call backend when initialized is undefined', () => { - expect.assertions(2); - - const table = { id: 'yVJPtuSackF' }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.COLLAPSE_TABLE, - table, - }, - ]; - return store - .dispatch(actions.collapseTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - }); - }); - - test('does not call backend when feature flag is off', () => { - expect.assertions(2); - - isFeatureEnabledMock.mockImplementation( - (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), - ); - - const table = { id: 1, initialized: true }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.COLLAPSE_TABLE, - table, - }, - ]; - return store - .dispatch(actions.collapseTable(table as unknown as Table)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - const expandedCalls = fetchMock.callHistory - .calls() - .filter( - call => - call.url && - call.url.includes('/tableschemaview/') && - call.url.includes('/expanded'), - ); - expect(expandedCalls).toHaveLength(0); - isFeatureEnabledMock.mockRestore(); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('removeTables', () => { - test('updates the table schema state in the backend', () => { - expect.assertions(2); - - const table = { id: 1, initialized: true }; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.REMOVE_TABLES, - tables: [table], - }, - ]; - return store - .dispatch(actions.removeTables([table] as unknown as Table[])) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTableSchemaEndpoint), - ).toHaveLength(1); - }); - }); - - test('deletes multiple tables and updates the table schema state in the backend', () => { - expect.assertions(2); - - const tables = [ - { id: 1, initialized: true }, - { id: 2, initialized: true }, - ]; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.REMOVE_TABLES, - tables, - }, - ]; - return store - .dispatch(actions.removeTables(tables as unknown as Table[])) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTableSchemaEndpoint), - ).toHaveLength(2); - }); - }); - - test('only updates the initialized table schema state in the backend', () => { - expect.assertions(2); - - const tables = [{ id: 1 }, { id: 2, initialized: true }]; - const store = mockStore({}); - const expectedActions = [ - { - type: actions.REMOVE_TABLES, - tables, - }, - ]; - return store - .dispatch(actions.removeTables(tables as unknown as Table[])) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTableSchemaEndpoint), - ).toHaveLength(1); - }); - }); - }); - - // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks - describe('syncQueryEditor', () => { - test('updates the tab state in the backend', () => { - expect.assertions(3); - - const results = { - data: mockBigNumber, - query: { sqlEditorId: 'null' }, - query_id: 'efgh', - }; - fetchMock.removeRoute(runQueryEndpoint); - fetchMock.post(runQueryEndpoint, JSON.stringify(results), { - name: runQueryEndpoint, - }); - - const oldQueryEditor = { ...queryEditor, inLocalStorage: true }; - const tables = [ - { - id: 'one', - dataPreviewQueryId: 'previewOne', - queryEditorId: oldQueryEditor.id, - inLocalStorage: true, - }, - { - id: 'two', - dataPreviewQueryId: 'previewTwo', - queryEditorId: oldQueryEditor.id, - inLocalStorage: true, - }, - ]; - const queries = [ - { - ...query, - id: 'previewOne', - sqlEditorId: oldQueryEditor.id, - inLocalStorage: true, - }, - { - ...query, - id: 'previewTwo', - sqlEditorId: oldQueryEditor.id, - inLocalStorage: true, - }, - ]; - const store = mockStore({ - sqlLab: { - queries, - tables, - }, - }); - const expectedActions = [ - { - type: actions.MIGRATE_QUERY_EDITOR, - oldQueryEditor, - // new qe has a different id - newQueryEditor: { - ...oldQueryEditor, - tabViewId: '1', - inLocalStorage: false, - loaded: true, - }, - }, - { - type: actions.MIGRATE_TABLE, - oldTable: tables[0], - // new table has a different id and points to new query editor - newTable: { ...tables[0], id: 1, queryEditorId: '1' }, - }, - { - type: actions.MIGRATE_TABLE, - oldTable: tables[1], - // new table has a different id and points to new query editor - newTable: { ...tables[1], id: 1, queryEditorId: '1' }, - }, - { - type: actions.MIGRATE_QUERY, - queryId: 'previewOne', - queryEditorId: '1', - }, - { - type: actions.MIGRATE_QUERY, - queryId: 'previewTwo', - queryEditorId: '1', - }, - ]; - return store - .dispatch(actions.syncQueryEditor(oldQueryEditor)) - .then(() => { - expect(store.getActions()).toEqual(expectedActions); - expect( - fetchMock.callHistory.calls(updateTabStateEndpoint), - ).toHaveLength(3); - - // query editor has 2 tables loaded in the schema viewer - expect( - fetchMock.callHistory.calls(updateTableSchemaEndpoint), - ).toHaveLength(2); - }); + expect(fetchMock.callHistory.calls(runQueryEndpoint)).toHaveLength(1); + // tab state is not updated, since the query is a data preview + expect( + fetchMock.callHistory.calls(updateTabStateEndpoint), + ).toHaveLength(0); }); }); }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('expandTable', () => { + test('updates the table schema state in the backend when initialized', () => { + expect.assertions(2); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store + .dispatch(actions.expandTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(1); + }); + }); + + test('does not call backend when table is not initialized', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF', initialized: false }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store + .dispatch(actions.expandTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when initialized is undefined', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF' }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store + .dispatch(actions.expandTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when feature flag is off', () => { + expect.assertions(2); + + isFeatureEnabledMock.mockImplementation( + (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), + ); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.EXPAND_TABLE, + table, + }, + ]; + return store + .dispatch(actions.expandTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + // Check all POST calls to find the expanded endpoint + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + isFeatureEnabledMock.mockRestore(); + }); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('collapseTable', () => { + test('updates the table schema state in the backend when initialized', () => { + expect.assertions(2); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store + .dispatch(actions.collapseTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(1); + }); + }); + + test('does not call backend when table is not initialized', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF', initialized: false }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store + .dispatch(actions.collapseTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when initialized is undefined', () => { + expect.assertions(2); + + const table = { id: 'yVJPtuSackF' }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store + .dispatch(actions.collapseTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + }); + }); + + test('does not call backend when feature flag is off', () => { + expect.assertions(2); + + isFeatureEnabledMock.mockImplementation( + (feature: string) => !(feature === 'SQLLAB_BACKEND_PERSISTENCE'), + ); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.COLLAPSE_TABLE, + table, + }, + ]; + return store + .dispatch(actions.collapseTable(table as unknown as Table)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + const expandedCalls = fetchMock.callHistory + .calls() + .filter( + call => + call.url && + call.url.includes('/tableschemaview/') && + call.url.includes('/expanded'), + ); + expect(expandedCalls).toHaveLength(0); + isFeatureEnabledMock.mockRestore(); + }); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('removeTables', () => { + test('updates the table schema state in the backend', () => { + expect.assertions(2); + + const table = { id: 1, initialized: true }; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.REMOVE_TABLES, + tables: [table], + }, + ]; + return store + .dispatch(actions.removeTables([table] as unknown as Table[])) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTableSchemaEndpoint), + ).toHaveLength(1); + }); + }); + + test('deletes multiple tables and updates the table schema state in the backend', () => { + expect.assertions(2); + + const tables = [ + { id: 1, initialized: true }, + { id: 2, initialized: true }, + ]; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.REMOVE_TABLES, + tables, + }, + ]; + return store + .dispatch(actions.removeTables(tables as unknown as Table[])) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTableSchemaEndpoint), + ).toHaveLength(2); + }); + }); + + test('only updates the initialized table schema state in the backend', () => { + expect.assertions(2); + + const tables = [{ id: 1 }, { id: 2, initialized: true }]; + const store = mockStore({}); + const expectedActions = [ + { + type: actions.REMOVE_TABLES, + tables, + }, + ]; + return store + .dispatch(actions.removeTables(tables as unknown as Table[])) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTableSchemaEndpoint), + ).toHaveLength(1); + }); + }); + }); + + // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks + describe('syncQueryEditor', () => { + test('updates the tab state in the backend', () => { + expect.assertions(3); + + const results = { + data: mockBigNumber, + query: { sqlEditorId: 'null' }, + query_id: 'efgh', + }; + fetchMock.removeRoute(runQueryEndpoint); + fetchMock.post(runQueryEndpoint, JSON.stringify(results), { + name: runQueryEndpoint, + }); + + const oldQueryEditor = { ...queryEditor, inLocalStorage: true }; + const tables = [ + { + id: 'one', + dataPreviewQueryId: 'previewOne', + queryEditorId: oldQueryEditor.id, + inLocalStorage: true, + }, + { + id: 'two', + dataPreviewQueryId: 'previewTwo', + queryEditorId: oldQueryEditor.id, + inLocalStorage: true, + }, + ]; + const queries = [ + { + ...query, + id: 'previewOne', + sqlEditorId: oldQueryEditor.id, + inLocalStorage: true, + }, + { + ...query, + id: 'previewTwo', + sqlEditorId: oldQueryEditor.id, + inLocalStorage: true, + }, + ]; + const store = mockStore({ + sqlLab: { + queries, + tables, + }, + }); + const expectedActions = [ + { + type: actions.MIGRATE_QUERY_EDITOR, + oldQueryEditor, + // new qe has a different id + newQueryEditor: { + ...oldQueryEditor, + tabViewId: '1', + inLocalStorage: false, + loaded: true, + }, + }, + { + type: actions.MIGRATE_TABLE, + oldTable: tables[0], + // new table has a different id and points to new query editor + newTable: { ...tables[0], id: 1, queryEditorId: '1' }, + }, + { + type: actions.MIGRATE_TABLE, + oldTable: tables[1], + // new table has a different id and points to new query editor + newTable: { ...tables[1], id: 1, queryEditorId: '1' }, + }, + { + type: actions.MIGRATE_QUERY, + queryId: 'previewOne', + queryEditorId: '1', + }, + { + type: actions.MIGRATE_QUERY, + queryId: 'previewTwo', + queryEditorId: '1', + }, + ]; + return store + .dispatch(actions.syncQueryEditor(oldQueryEditor)) + .then(() => { + expect(store.getActions()).toEqual(expectedActions); + expect( + fetchMock.callHistory.calls(updateTabStateEndpoint), + ).toHaveLength(3); + + // query editor has 2 tables loaded in the schema viewer + expect( + fetchMock.callHistory.calls(updateTableSchemaEndpoint), + ).toHaveLength(2); + }); + }); + }); }); diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts b/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts index 4fcf7132b3c..dc88812891b 100644 --- a/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts +++ b/superset-frontend/src/SqlLab/components/EditorWrapper/useAnnotations.test.ts @@ -19,6 +19,7 @@ import fetchMock from 'fetch-mock'; import { act, renderHook } from '@testing-library/react-hooks'; import { COMMON_ERR_MESSAGES } from '@superset-ui/core'; +import { vi } from 'vitest'; import { createWrapper, defaultStore as store, @@ -44,8 +45,8 @@ const expectTemplateParams = '{"a": 1, "v": "str"}'; const expectValidatorEngine = 'defined_validator'; const queryValidationApiRoute = `glob:*/api/v1/database/${expectDbId}/validate_sql/`; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), +vi.mock(import('@superset-ui/core'), async importOriginal => ({ + ...(await importOriginal()), t: (str: string) => str, })); diff --git a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts index 60c6d5095f5..a8ec5c9d609 100644 --- a/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts +++ b/superset-frontend/src/SqlLab/components/EditorWrapper/useKeywords.test.ts @@ -37,6 +37,7 @@ import { SQL_FUNCTIONS_AUTOCOMPLETE_SCORE, } from 'src/SqlLab/constants'; import { useKeywords } from './useKeywords'; +import { afterEach, beforeEach, test } from 'vitest'; const fakeSchemaApiResult = ['schema1', 'schema2']; const fakeTableApiResult = { @@ -153,6 +154,7 @@ test('returns keywords including fetched function_names data', async () => { }); test('skip fetching if autocomplete skipped', () => { + fetchMock.clearHistory().removeRoutes(); const { result } = renderHook( () => useKeywords( diff --git a/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts b/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts index 820f820be30..e05ae528491 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts +++ b/superset-frontend/src/SqlLab/components/SqlEditorTopBar/useDatabaseSelector.test.ts @@ -24,6 +24,7 @@ import { initialState, defaultQueryEditor } from 'src/SqlLab/fixtures'; import * as localStorageHelpers from 'src/utils/localStorageHelpers'; import useDatabaseSelector from './useDatabaseSelector'; +import { vi } from 'vitest'; const middlewares = [thunk]; const mockStore = configureStore(middlewares); @@ -48,13 +49,13 @@ const createInitialState = (overrides = {}) => ({ }); beforeEach(() => { - jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(null); - jest.spyOn(localStorageHelpers, 'setItem').mockImplementation(() => {}); + vi.spyOn(localStorageHelpers, 'getItem').mockReturnValue(null); + vi.spyOn(localStorageHelpers, 'setItem').mockImplementation(() => {}); }); afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); + vi.clearAllMocks(); + vi.restoreAllMocks(); }); test('returns initial values from query editor', () => { @@ -242,7 +243,7 @@ test('reads database from localStorage when URL has db param', () => { backend: 'sqlite', }; - jest.spyOn(localStorageHelpers, 'getItem').mockReturnValue(localStorageDb); + vi.spyOn(localStorageHelpers, 'getItem').mockReturnValue(localStorageDb); const originalLocation = window.location; Object.defineProperty(window, 'location', { diff --git a/superset-frontend/src/SqlLab/fixtures.ts b/superset-frontend/src/SqlLab/fixtures.ts index 05bd77b4af7..3b6d99716c5 100644 --- a/superset-frontend/src/SqlLab/fixtures.ts +++ b/superset-frontend/src/SqlLab/fixtures.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { Mock, vi } from 'vitest'; import * as actions from 'src/SqlLab/actions/sqlLab'; import { ColumnKeyTypeType } from 'src/SqlLab/components/ColumnElement'; import { @@ -29,8 +30,8 @@ import { GenericDataType } from '@apache-superset/core/api/core'; import { LatestQueryEditorVersion } from 'src/SqlLab/types'; import { ISaveableDatasource } from 'src/SqlLab/components/SaveDatasetModal'; -export const mockedActions = Object.fromEntries( - Object.keys(actions).map(key => [key, jest.fn()]), +export const mockedActions: Record = Object.fromEntries( + Object.keys(actions).map(key => [key, vi.fn()]), ); export const alert = { bsStyle: 'danger', msg: 'Oops', id: 'lksvmcx32' }; diff --git a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/transformProps.test.ts b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/transformProps.test.ts index 65fb401bd6a..b9926ff7740 100644 --- a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/transformProps.test.ts +++ b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/transformProps.test.ts @@ -21,7 +21,7 @@ import transformProps from './transformProps'; import { DeckglLayerVisibilityFormData } from './types'; test('transforms props correctly with all required fields', () => { - const setDataMaskMock = jest.fn(); + const setDataMaskMock = vi.fn(); const formData: DeckglLayerVisibilityFormData = { viz_type: 'deckgl_layer_visibility', defaultToAllLayersVisible: true, @@ -50,7 +50,7 @@ test('transforms props correctly with all required fields', () => { }); test('transforms props with empty filter state', () => { - const setDataMaskMock = jest.fn(); + const setDataMaskMock = vi.fn(); const formData: DeckglLayerVisibilityFormData = { viz_type: 'deckgl_layer_visibility', defaultToAllLayersVisible: false, @@ -79,7 +79,7 @@ test('transforms props with empty filter state', () => { }); test('preserves setDataMask function reference', () => { - const setDataMaskMock = jest.fn(); + const setDataMaskMock = vi.fn(); const formData: DeckglLayerVisibilityFormData = { viz_type: 'deckgl_layer_visibility', defaultToAllLayersVisible: true, diff --git a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts index 4b19a795f59..5fa6dc1cd74 100644 --- a/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts +++ b/superset-frontend/src/chartCustomizations/components/DeckglLayerVisibility/useDeckLayerMetadata.test.ts @@ -20,17 +20,17 @@ import { renderHook } from '@testing-library/react-hooks'; import { SupersetClient } from '@superset-ui/core'; import { useDeckLayerMetadata } from './useDeckLayerMetadata'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), SupersetClient: { - get: jest.fn(), + get: vi.fn(), }, })); -const mockSupersetClientGet = SupersetClient.get as jest.Mock; +const mockSupersetClientGet = SupersetClient.get as vi.Mock; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('returns empty layers when sliceIds is empty', () => { diff --git a/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.test.ts b/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.test.ts index 59a5505a76e..2c1a4962e0b 100644 --- a/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.test.ts +++ b/superset-frontend/src/components/Chart/DrillBy/useResultsTableView.test.ts @@ -29,12 +29,10 @@ import { useResultsTableView } from './useResultsTableView'; const capturedProps: any[] = []; -jest.mock( +vi.mock( 'src/explore/components/DataTablesPane/components/SingleQueryResultPane', - () => { - const actual = jest.requireActual( - 'src/explore/components/DataTablesPane/components/SingleQueryResultPane', - ); + async (importActual) => { + const actual = await importActual() as any; return { ...actual, SingleQueryResultPane: (props: any) => { diff --git a/superset-frontend/src/components/Chart/chartActions.test.ts b/superset-frontend/src/components/Chart/chartActions.test.ts index f91a9d75194..ae016c1a64f 100644 --- a/superset-frontend/src/components/Chart/chartActions.test.ts +++ b/superset-frontend/src/components/Chart/chartActions.test.ts @@ -41,6 +41,7 @@ import * as dataMaskActions from 'src/dataMask/actions'; import configureMockStore from 'redux-mock-store'; import thunk from 'redux-thunk'; import { initialState } from 'src/SqlLab/fixtures'; +import { Mock, vi } from 'vitest'; interface MockState { charts: { @@ -76,29 +77,27 @@ const mockGetState = (): MockState => ({ }, }); -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getChartMetadataRegistry: jest.fn(), - getChartBuildQueryRegistry: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getChartMetadataRegistry: vi.fn(), + getChartBuildQueryRegistry: vi.fn(), })); -const mockedGetChartMetadataRegistry = - getChartMetadataRegistry as jest.MockedFunction< - typeof getChartMetadataRegistry - >; -const mockedGetChartBuildQueryRegistry = - getChartBuildQueryRegistry as jest.MockedFunction< - typeof getChartBuildQueryRegistry - >; +const mockedGetChartMetadataRegistry = getChartMetadataRegistry as Mock< + typeof getChartMetadataRegistry +>; +const mockedGetChartBuildQueryRegistry = getChartBuildQueryRegistry as Mock< + typeof getChartBuildQueryRegistry +>; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('chart actions', () => { const MOCK_URL = '/mockURL'; - let dispatch: jest.Mock; - let getExploreUrlStub: jest.SpyInstance; - let getChartDataUriStub: jest.SpyInstance; - let buildV1ChartDataPayloadStub: jest.SpyInstance; - let waitForAsyncDataStub: jest.SpyInstance; + let dispatch: Mock; + let getExploreUrlStub: Mock; + let getChartDataUriStub: Mock; + let buildV1ChartDataPayloadStub: Mock; + let waitForAsyncDataStub: Mock; let fakeMetadata: { useLegacyApi?: boolean; viz_type?: string }; beforeAll(() => { @@ -116,16 +115,16 @@ describe('chart actions', () => { afterEach(() => fetchMock.clearHistory().removeRoutes()); beforeEach(() => { - dispatch = jest.fn(); - getExploreUrlStub = jest + dispatch = vi.fn(); + getExploreUrlStub = vi .spyOn(exploreUtils, 'getExploreUrl') .mockImplementation(() => MOCK_URL); - getChartDataUriStub = jest + getChartDataUriStub = vi .spyOn(exploreUtils, 'getChartDataUri') .mockImplementation(({ qs }: { qs?: Record }) => URI(MOCK_URL).query(qs || {}), ); - buildV1ChartDataPayloadStub = jest + buildV1ChartDataPayloadStub = vi .spyOn(exploreUtils, 'buildV1ChartDataPayload') .mockResolvedValue({ some_param: 'fake query!', @@ -151,13 +150,13 @@ describe('chart actions', () => { }), }) as unknown as ReturnType, ); - waitForAsyncDataStub = jest + waitForAsyncDataStub = vi .spyOn(asyncEvent, 'waitForAsyncData') .mockImplementation((data: unknown) => Promise.resolve(data)); }); test('should defer abort of previous controller to avoid Redux state mutation', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const chartKey = 'defer_abort_test'; const formData: Partial = { slice_id: 123, @@ -165,7 +164,7 @@ describe('chart actions', () => { viz_type: 'table', }; const oldController = new AbortController(); - const abortSpy = jest.spyOn(oldController, 'abort'); + const abortSpy = vi.spyOn(oldController, 'abort'); const state: MockState = { charts: { [chartKey]: { @@ -178,23 +177,23 @@ describe('chart actions', () => { }, }, }; - const getState = jest.fn(() => state); - const dispatchMock = jest.fn(); - const getChartDataRequestSpy = jest + const getState = vi.fn(() => state); + const dispatchMock = vi.fn(); + const getChartDataRequestSpy = vi .spyOn(actions, 'getChartDataRequest') .mockResolvedValue({ response: { status: 200 } as Response, json: { result: [] }, }); - const handleChartDataResponseSpy = jest + const handleChartDataResponseSpy = vi .spyOn(actions, 'handleChartDataResponse') .mockResolvedValue([]); - const updateDataMaskSpy = jest + const updateDataMaskSpy = vi .spyOn(dataMaskActions, 'updateDataMask') .mockReturnValue({ type: 'UPDATE_DATA_MASK' } as ReturnType< typeof dataMaskActions.updateDataMask >); - const getQuerySettingsStub = jest + const getQuerySettingsStub = vi .spyOn(exploreUtils, 'getQuerySettings') .mockReturnValue([false, () => {}] as unknown as ReturnType< typeof exploreUtils.getQuerySettings @@ -216,7 +215,7 @@ describe('chart actions', () => { expect(abortSpy).not.toHaveBeenCalled(); expect(oldController.signal.aborted).toBe(false); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(abortSpy).toHaveBeenCalledTimes(1); expect(oldController.signal.aborted).toBe(true); @@ -228,7 +227,7 @@ describe('chart actions', () => { updateDataMaskSpy.mockRestore(); getQuerySettingsStub.mockRestore(); abortSpy.mockRestore(); - jest.useRealTimers(); + vi.useRealTimers(); } }); @@ -283,7 +282,7 @@ describe('chart actions', () => { fetchMock.post(mockBigIntUrl, `{ "value": ${expectedBigNumber} }`, { name: mockBigIntUrl, }); - getChartDataUriStub = jest + getChartDataUriStub = vi .spyOn(exploreUtils, 'getChartDataUri') .mockImplementation(() => URI(mockBigIntUrl)); @@ -541,7 +540,7 @@ describe('chart actions', () => { fetchMock.post(mockBigIntUrl, `{ "value": ${expectedBigNumber} }`, { name: mockBigIntUrl, }); - getExploreUrlStub = jest + getExploreUrlStub = vi .spyOn(exploreUtils, 'getExploreUrl') .mockImplementation(() => mockBigIntUrl); @@ -559,9 +558,9 @@ describe('chart actions', () => { // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('runAnnotationQuery', () => { - const mockDispatch = jest.fn(); + const mockDispatch = vi.fn(); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('should dispatch annotationQueryStarted and annotationQuerySuccess on successful query', async () => { @@ -588,14 +587,14 @@ describe('chart actions', () => { } as AnnotationLayer; const key = undefined; - const postSpy = jest.spyOn(SupersetClient, 'post'); + const postSpy = vi.spyOn(SupersetClient, 'post'); postSpy.mockImplementation( () => Promise.resolve({ json: { result: [] } }) as unknown as ReturnType< typeof SupersetClient.post >, ); - const buildV1ChartDataPayloadSpy = jest.spyOn( + const buildV1ChartDataPayloadSpy = vi.spyOn( exploreUtils, 'buildV1ChartDataPayload', ); @@ -624,11 +623,11 @@ describe('chart actions', () => { // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('chart actions timeout', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('should use the timeout from arguments when given', async () => { - const postSpy = jest.spyOn(SupersetClient, 'post'); + const postSpy = vi.spyOn(SupersetClient, 'post'); postSpy.mockImplementation( () => Promise.resolve({ json: { result: [] } }) as unknown as ReturnType< @@ -666,7 +665,7 @@ describe('chart actions timeout', () => { }); test('should use the timeout from common.conf when not passed as an argument', async () => { - const postSpy = jest.spyOn(SupersetClient, 'post'); + const postSpy = vi.spyOn(SupersetClient, 'post'); postSpy.mockImplementation( () => Promise.resolve({ json: { result: [] } }) as unknown as ReturnType< diff --git a/superset-frontend/src/components/Modal/useModalValidation.test.ts b/superset-frontend/src/components/Modal/useModalValidation.test.ts index f4de28133b1..f83b9a1db2d 100644 --- a/superset-frontend/src/components/Modal/useModalValidation.test.ts +++ b/superset-frontend/src/components/Modal/useModalValidation.test.ts @@ -27,17 +27,17 @@ const mockSections = [ { key: 'section1', name: 'Section One', - validator: jest.fn(() => []), + validator: vi.fn(() => []), }, { key: 'section2', name: 'Section Two', - validator: jest.fn(() => []), + validator: vi.fn(() => []), }, ]; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('initializes with no errors', () => { @@ -123,7 +123,7 @@ test('returns true when all sections are valid', () => { }); test('calls onValidationChange when validation state changes', () => { - const onValidationChange = jest.fn(); + const onValidationChange = vi.fn(); mockSections[0].validator.mockReturnValue(['Error']); const { result } = renderHook(() => diff --git a/superset-frontend/src/components/StreamingExportModal/useStreamingExport.test.ts b/superset-frontend/src/components/StreamingExportModal/useStreamingExport.test.ts index 8f114c6e462..4035b316348 100644 --- a/superset-frontend/src/components/StreamingExportModal/useStreamingExport.test.ts +++ b/superset-frontend/src/components/StreamingExportModal/useStreamingExport.test.ts @@ -16,41 +16,38 @@ * specific language governing permissions and limitations * under the License. */ -import { TextEncoder, TextDecoder } from 'util'; import { renderHook, act } from '@testing-library/react-hooks'; import { waitFor } from '@testing-library/react'; import { useStreamingExport } from './useStreamingExport'; import { ExportStatus } from './StreamingExportModal'; -// Polyfill TextEncoder/TextDecoder for Jest environment -global.TextEncoder = TextEncoder; -global.TextDecoder = TextDecoder as typeof global.TextDecoder; - // Mock SupersetClient -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), SupersetClient: { - getCSRFToken: jest.fn(() => Promise.resolve('mock-csrf-token')), + getCSRFToken: vi.fn(() => Promise.resolve('mock-csrf-token')), }, })); +const { applicationRoot, makeUrl } = vi.hoisted(() => ({ applicationRoot: vi.fn(), makeUrl: vi.fn((path: string) => path) })); + // Mock pathUtils and getBootstrapData for URL prefix guard tests -jest.mock('src/utils/pathUtils', () => ({ - makeUrl: jest.fn((path: string) => path), +vi.mock('src/utils/pathUtils', () => ({ + makeUrl: makeUrl, })); -jest.mock('src/utils/getBootstrapData', () => ({ - applicationRoot: jest.fn(() => ''), +vi.mock('src/utils/getBootstrapData', () => ({ + applicationRoot: applicationRoot, })); -global.URL.createObjectURL = jest.fn(() => 'blob:mock-url'); -global.URL.revokeObjectURL = jest.fn(); +global.URL.createObjectURL = vi.fn(() => 'blob:mock-url'); +global.URL.revokeObjectURL = vi.fn(); -global.fetch = jest.fn(); +global.fetch = vi.fn(); beforeEach(() => { - jest.clearAllMocks(); - global.fetch = jest.fn(); + vi.clearAllMocks(); + global.fetch = vi.fn(); }); test('useStreamingExport initializes with default progress state', () => { @@ -104,14 +101,14 @@ test('useStreamingExport resetExport resets progress to initial state', () => { }); test('useStreamingExport accepts onComplete callback option', () => { - const onComplete = jest.fn(); + const onComplete = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onComplete })); expect(result.current).toBeDefined(); }); test('useStreamingExport accepts onError callback option', () => { - const onError = jest.fn(); + const onError = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onError })); expect(result.current).toBeDefined(); @@ -130,7 +127,7 @@ test('useStreamingExport progress includes all required fields', () => { }); test('useStreamingExport cleans up on unmount', () => { - const revokeObjectURL = jest.fn(); + const revokeObjectURL = vi.fn(); global.URL.revokeObjectURL = revokeObjectURL; const { unmount } = renderHook(() => useStreamingExport()); @@ -145,14 +142,14 @@ test('retryExport reuses the same URL from the original startExport call', async // This test ensures that retryExport uses the exact same URL that was passed to startExport, // which is important for subdirectory deployments where the URL is already prefixed. const originalUrl = '/superset/api/v1/sqllab/export_streaming/'; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockResolvedValue({ done: true, value: undefined }), + read: vi.fn().mockResolvedValue({ done: true, value: undefined }), }), }, }); @@ -197,10 +194,10 @@ test('retryExport reuses the same URL from the original startExport call', async test('sets ERROR status and calls onError when fetch rejects', async () => { const errorMessage = 'Network error'; - const mockFetch = jest.fn().mockRejectedValue(new Error(errorMessage)); + const mockFetch = vi.fn().mockRejectedValue(new Error(errorMessage)); global.fetch = mockFetch; - const onError = jest.fn(); + const onError = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onError })); act(() => { @@ -222,19 +219,15 @@ test('sets ERROR status and calls onError when fetch rejects', async () => { expect(onError).toHaveBeenCalledWith(errorMessage); }); -// URL prefix guard tests - prevent regression of missing app root prefix -const { applicationRoot } = jest.requireMock('src/utils/getBootstrapData'); -const { makeUrl } = jest.requireMock('src/utils/pathUtils'); - const createPrefixTestMockFetch = () => - jest.fn().mockResolvedValue({ + vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockResolvedValue({ done: true, value: undefined }), + read: vi.fn().mockResolvedValue({ done: true, value: undefined }), }), }, }); @@ -530,14 +523,14 @@ test('sets ERROR status and calls onError when stream contains __STREAM_ERROR__ ); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: errorChunk }); @@ -549,7 +542,7 @@ test('sets ERROR status and calls onError when stream contains __STREAM_ERROR__ }); global.fetch = mockFetch; - const onError = jest.fn(); + const onError = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onError })); act(() => { @@ -574,14 +567,14 @@ test('completes CSV export successfully with correct status and downloadUrl', as const csvData = new TextEncoder().encode('id,name\n1,Alice\n2,Bob\n'); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="results.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: csvData }); @@ -593,7 +586,7 @@ test('completes CSV export successfully with correct status and downloadUrl', as }); global.fetch = mockFetch; - const onComplete = jest.fn(); + const onComplete = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onComplete })); act(() => { @@ -619,14 +612,14 @@ test('completes XLSX export successfully with correct filename', async () => { const xlsxData = new Uint8Array([0x50, 0x4b, 0x03, 0x04]); // XLSX magic bytes let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="report.xlsx"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: xlsxData }); @@ -638,7 +631,7 @@ test('completes XLSX export successfully with correct filename', async () => { }); global.fetch = mockFetch; - const onComplete = jest.fn(); + const onComplete = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onComplete })); act(() => { @@ -659,7 +652,7 @@ test('completes XLSX export successfully with correct filename', async () => { test('sets ERROR status when response is not ok (4xx/5xx)', async () => { applicationRoot.mockReturnValue(''); - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 500, statusText: 'Internal Server Error', @@ -667,7 +660,7 @@ test('sets ERROR status when response is not ok (4xx/5xx)', async () => { }); global.fetch = mockFetch; - const onError = jest.fn(); + const onError = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onError })); act(() => { @@ -692,14 +685,14 @@ test('sets ERROR status when response is not ok (4xx/5xx)', async () => { test('sets ERROR status when response body is missing', async () => { applicationRoot.mockReturnValue(''); - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({}), body: null, }); global.fetch = mockFetch; - const onError = jest.fn(); + const onError = vi.fn(); const { result } = renderHook(() => useStreamingExport({ onError })); act(() => { @@ -727,7 +720,7 @@ test('cancelExport sets CANCELLED status and aborts the request', async () => { let abortSignal: AbortSignal | undefined; // Create a reader that will hang until aborted - const mockFetch = jest.fn().mockImplementation((_url, options) => { + const mockFetch = vi.fn().mockImplementation((_url, options) => { abortSignal = options?.signal; return Promise.resolve({ ok: true, @@ -736,7 +729,7 @@ test('cancelExport sets CANCELLED status and aborts the request', async () => { }), body: { getReader: () => ({ - read: jest.fn().mockImplementation( + read: vi.fn().mockImplementation( () => new Promise((resolve, reject) => { // Simulate slow stream that can be aborted @@ -788,14 +781,14 @@ test('parses filename from Content-Disposition header with quotes', async () => const csvData = new TextEncoder().encode('data\n'); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="my export file.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: csvData }); @@ -829,12 +822,12 @@ test('uses default filename when Content-Disposition header is missing', async ( const csvData = new TextEncoder().encode('data\n'); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({}), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: csvData }); @@ -869,14 +862,14 @@ test('updates progress with rowsProcessed and totalSize during streaming', async const chunk2 = new TextEncoder().encode('2,Bob\n3,Charlie\n'); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: chunk1 }); @@ -917,14 +910,14 @@ test('prevents double startExport calls while export is in progress', async () = // Create a slow reader that takes time to complete let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation( + read: vi.fn().mockImplementation( () => new Promise(resolve => { readCount += 1; @@ -977,7 +970,7 @@ test('prevents double startExport calls while export is in progress', async () = test('retryExport does nothing when no prior export exists', async () => { applicationRoot.mockReturnValue(''); - const mockFetch = jest.fn(); + const mockFetch = vi.fn(); global.fetch = mockFetch; const { result } = renderHook(() => useStreamingExport()); @@ -1001,14 +994,14 @@ test('state resets correctly after successful export and resetExport call', asyn const csvData = new TextEncoder().encode('data\n'); let readCount = 0; - const mockFetch = jest.fn().mockResolvedValue({ + const mockFetch = vi.fn().mockResolvedValue({ ok: true, headers: new Headers({ 'Content-Disposition': 'attachment; filename="export.csv"', }), body: { getReader: () => ({ - read: jest.fn().mockImplementation(() => { + read: vi.fn().mockImplementation(() => { readCount += 1; if (readCount === 1) { return Promise.resolve({ done: false, value: csvData }); @@ -1053,7 +1046,7 @@ test('state resets correctly after successful export and resetExport call', asyn test('state resets correctly after failed export and resetExport call', async () => { applicationRoot.mockReturnValue(''); - const mockFetch = jest.fn().mockRejectedValue(new Error('Network error')); + const mockFetch = vi.fn().mockRejectedValue(new Error('Network error')); global.fetch = mockFetch; const { result } = renderHook(() => useStreamingExport()); diff --git a/superset-frontend/src/core/editors/EditorProviders.test.ts b/superset-frontend/src/core/editors/EditorProviders.test.ts index 74abb6088cf..7578deaa754 100644 --- a/superset-frontend/src/core/editors/EditorProviders.test.ts +++ b/superset-frontend/src/core/editors/EditorProviders.test.ts @@ -42,7 +42,7 @@ function createMockEditorContribution( * Creates a mock editor component for testing. */ function createMockEditorComponent(): EditorComponent { - return jest.fn(() => null) as unknown as EditorComponent; + return vi.fn(() => null) as unknown as EditorComponent; } beforeEach(() => { @@ -166,7 +166,7 @@ test('supports multiple languages per provider', () => { test('warns when registering duplicate provider id', () => { const manager = EditorProviders.getInstance(); - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(); const contribution = createMockEditorContribution({ id: 'duplicate-editor', @@ -195,7 +195,7 @@ test('warns when registering duplicate provider id', () => { test('fires onDidRegister event when provider is registered', () => { const manager = EditorProviders.getInstance(); - const listener = jest.fn(); + const listener = vi.fn(); manager.onDidRegister(listener); @@ -215,7 +215,7 @@ test('fires onDidRegister event when provider is registered', () => { test('fires onDidUnregister event when provider is unregistered', () => { const manager = EditorProviders.getInstance(); - const listener = jest.fn(); + const listener = vi.fn(); manager.onDidUnregister(listener); @@ -235,7 +235,7 @@ test('fires onDidUnregister event when provider is unregistered', () => { test('event listeners can be disposed', () => { const manager = EditorProviders.getInstance(); - const listener = jest.fn(); + const listener = vi.fn(); const listenerDisposable = manager.onDidRegister(listener); @@ -261,12 +261,12 @@ test('event listeners can be disposed', () => { test('handles errors in event listeners gracefully', () => { const manager = EditorProviders.getInstance(); - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(); - const errorListener = jest.fn(() => { + const errorListener = vi.fn(() => { throw new Error('Listener error'); }); - const successListener = jest.fn(); + const successListener = vi.fn(); manager.onDidRegister(errorListener); manager.onDidRegister(successListener); diff --git a/superset-frontend/src/dashboard/actions/dashboardLayout.test.ts b/superset-frontend/src/dashboard/actions/dashboardLayout.test.ts index aed5e7b0026..f4de1bba682 100644 --- a/superset-frontend/src/dashboard/actions/dashboardLayout.test.ts +++ b/superset-frontend/src/dashboard/actions/dashboardLayout.test.ts @@ -17,6 +17,7 @@ * under the License. */ import { ActionCreators as UndoActionCreators } from 'redux-undo'; +import { vi, type Mock } from 'vitest'; import { UPDATE_COMPONENTS, @@ -73,17 +74,17 @@ describe('dashboardLayout actions', () => { }, }; - let updateLayoutComponentsSpy: jest.SpyInstance; + let updateLayoutComponentsSpy: Mock; function setup(stateOverrides: Record = {}) { const state = { ...mockState, ...stateOverrides }; - const getState = jest.fn(() => state) as unknown as GetState; - const dispatch = jest.fn(); + const getState = vi.fn(() => state) as unknown as GetState; + const dispatch = vi.fn(); return { getState, dispatch, state }; } beforeEach(() => { - updateLayoutComponentsSpy = jest.spyOn( + updateLayoutComponentsSpy = vi.spyOn( dashboardFilters, 'updateLayoutComponents', ); diff --git a/superset-frontend/src/dashboard/actions/dashboardState.test.ts b/superset-frontend/src/dashboard/actions/dashboardState.test.ts index 7f8b3b53d4a..84950cf60a8 100644 --- a/superset-frontend/src/dashboard/actions/dashboardState.test.ts +++ b/superset-frontend/src/dashboard/actions/dashboardState.test.ts @@ -18,6 +18,7 @@ */ import { SupersetClient, isFeatureEnabled } from '@superset-ui/core'; import { waitFor } from 'spec/helpers/testing-library'; +import { vi, Mock } from 'vitest'; import { SAVE_DASHBOARD_STARTED, @@ -39,18 +40,18 @@ import { emptyFilters } from 'spec/fixtures/mockDashboardFilters'; import mockDashboardData from 'spec/fixtures/mockDashboardData'; import { navigateTo } from 'src/utils/navigationUtils'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - isFeatureEnabled: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + isFeatureEnabled: vi.fn(), })); -jest.mock('src/utils/navigationUtils', () => ({ - navigateTo: jest.fn(), - navigateWithState: jest.fn(), +vi.mock('src/utils/navigationUtils', () => ({ + navigateTo: vi.fn(), + navigateWithState: vi.fn(), })); -const mockIsFeatureEnabled = isFeatureEnabled as jest.Mock; -const mockNavigateTo = navigateTo as jest.Mock; +const mockIsFeatureEnabled = isFeatureEnabled as Mock; +const mockNavigateTo = navigateTo as Mock; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('dashboardState actions', () => { @@ -75,16 +76,16 @@ describe('dashboardState actions', () => { }; const newDashboardData = mockDashboardData; - let postStub: jest.SpyInstance; - let getStub: jest.SpyInstance; - let putStub: jest.SpyInstance; + let postStub: Mock; + let getStub: Mock; + let putStub: Mock; const updatedCss = '.updated_css_value {\n color: black;\n}'; beforeEach(() => { - postStub = jest + postStub = vi .spyOn(SupersetClient, 'post') .mockResolvedValue('the value you want to return' as any); - getStub = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ + getStub = vi.spyOn(SupersetClient, 'get').mockResolvedValue({ json: { result: { ...mockDashboardData, @@ -92,7 +93,7 @@ describe('dashboardState actions', () => { }, }, } as any); - putStub = jest.spyOn(SupersetClient, 'put').mockResolvedValue({ + putStub = vi.spyOn(SupersetClient, 'put').mockResolvedValue({ json: { result: mockDashboardData, }, @@ -106,8 +107,8 @@ describe('dashboardState actions', () => { function setup(stateOverrides: Record = {}) { const state = { ...mockState, ...stateOverrides }; - const getState = jest.fn(() => state) as unknown as () => any; - const dispatch = jest.fn(); + const getState = vi.fn(() => state) as unknown as () => any; + const dispatch = vi.fn(); return { getState, dispatch, state }; } @@ -214,7 +215,7 @@ describe('dashboardState actions', () => { }); postStub.mockRestore(); - postStub = jest.spyOn(SupersetClient, 'post').mockResolvedValue({ + postStub = vi.spyOn(SupersetClient, 'post').mockResolvedValue({ json: { result: { ...mockDashboardData, diff --git a/superset-frontend/src/dashboard/components/dnd/handleScroll/handleScroll.test.ts b/superset-frontend/src/dashboard/components/dnd/handleScroll/handleScroll.test.ts index ad77f64108d..4a3f980ead5 100644 --- a/superset-frontend/src/dashboard/components/dnd/handleScroll/handleScroll.test.ts +++ b/superset-frontend/src/dashboard/components/dnd/handleScroll/handleScroll.test.ts @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ + +import { vi } from 'vitest'; import handleScroll from '.'; -jest.useFakeTimers(); -jest.spyOn(global, 'clearInterval'); +const clearIntervalSpy = vi.spyOn(global, 'clearInterval'); const { scroll } = window; @@ -28,12 +29,12 @@ afterAll(() => { }); test('calling: "NOT_SCROLL_TOP" ,"SCROLL_TOP", "NOT_SCROLL_TOP"', () => { - window.scroll = jest.fn(); + window.scroll = vi.fn(); document.documentElement.scrollTop = 500; handleScroll('NOT_SCROLL_TOP'); - expect(clearInterval).not.toHaveBeenCalled(); + expect(clearIntervalSpy).not.toHaveBeenCalled(); handleScroll('SCROLL_TOP'); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/state.test.ts b/superset-frontend/src/dashboard/components/nativeFilters/state.test.ts index 782e0fff6bf..3575f87921a 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/state.test.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/state.test.ts @@ -19,19 +19,17 @@ import { renderHook } from '@testing-library/react-hooks'; import { useSelector } from 'react-redux'; +import { vi, type Mock } from 'vitest'; import { NativeFilterType, Filter, Divider } from '@superset-ui/core'; import { useIsFilterInScope, useSelectFiltersInScope } from './state'; -jest.mock('react-redux', () => { - const actual = jest.requireActual('react-redux'); - return { - ...actual, - useSelector: jest.fn(), - }; -}); +vi.mock('react-redux', async importActual => ({ + ...(await importActual()), + useSelector: vi.fn(), + })); beforeEach(() => { - (useSelector as jest.Mock).mockImplementation(selector => { + (useSelector as Mock).mockImplementation(selector => { if (selector.name === 'useActiveDashboardTabs') { return ['TAB_1']; } @@ -124,7 +122,7 @@ test('useSelectFiltersInScope should return all filters in scope when no tabs ex // Tests for filter scope persistence when chartsInScope is missing // (Bug fix: filters incorrectly marked out of scope after editing non-scope properties) test('filter without chartsInScope should fall back to rootPath check', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB_1'] }, dashboardLayout: { present: {} }, @@ -150,7 +148,7 @@ test('filter without chartsInScope should fall back to rootPath check', () => { }); test('filter with empty chartsInScope array should check rootPath', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB_1'] }, dashboardLayout: { present: {} }, @@ -177,7 +175,7 @@ test('filter with empty chartsInScope array should check rootPath', () => { }); test('filter without chartsInScope and inactive rootPath should be out of scope', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB_1'] }, dashboardLayout: { present: {} }, @@ -203,7 +201,7 @@ test('filter without chartsInScope and inactive rootPath should be out of scope' }); test('filter with ROOT_ID in rootPath should be in scope when chartsInScope is missing', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['ROOT_ID'] }, dashboardLayout: { present: {} }, @@ -229,7 +227,7 @@ test('filter with ROOT_ID in rootPath should be in scope when chartsInScope is m }); test('useSelectFiltersInScope correctly categorizes filters with missing chartsInScope', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB_1'] }, dashboardLayout: { @@ -298,7 +296,7 @@ test('filter with chartsInScope takes precedence over rootPath', () => { }); test('filter should be hidden on excluded nested tab even when parent tab is active', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB-Parent1', 'TAB-P1_Child2'] }, dashboardLayout: { @@ -352,7 +350,7 @@ test('filter should be hidden on excluded nested tab even when parent tab is act }); test('filter should be visible on included nested tab', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB-Parent1', 'TAB-P1_Child1'] }, dashboardLayout: { @@ -395,7 +393,7 @@ test('filter should be visible on included nested tab', () => { }); test('filter should be visible on top-level tab when charts have no nested parents', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB-Parent2'] }, dashboardLayout: { @@ -436,7 +434,7 @@ test('filter should be visible on top-level tab when charts have no nested paren }); test('filter with chartsInScope referencing non-existent chart should still work', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB-Parent1'] }, dashboardLayout: { @@ -472,7 +470,7 @@ test('filter with chartsInScope referencing non-existent chart should still work }); test('filter with mix of existing and non-existent charts in chartsInScope', () => { - (useSelector as jest.Mock).mockImplementation((selector: Function) => { + (useSelector as Mock).mockImplementation((selector: Function) => { const mockState = { dashboardState: { activeTabs: ['TAB-Parent2'] }, dashboardLayout: { diff --git a/superset-frontend/src/dashboard/reducers/dashboardFilters.test.ts b/superset-frontend/src/dashboard/reducers/dashboardFilters.test.ts index 0a81c7e4166..669021b007d 100644 --- a/superset-frontend/src/dashboard/reducers/dashboardFilters.test.ts +++ b/superset-frontend/src/dashboard/reducers/dashboardFilters.test.ts @@ -32,6 +32,7 @@ import { column, } from 'spec/fixtures/mockSliceEntities'; import { filterComponent } from 'spec/fixtures/mockDashboardLayout'; +import { vi } from 'vitest'; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('dashboardFilters reducer', () => { @@ -129,8 +130,9 @@ describe('dashboardFilters reducer', () => { [`${filterId}_region`]: regionScope, [`${filterId}_gender`]: genderScope, }; - (activeDashboardFilters as Record).buildActiveFilters = - jest.fn(); + vi.spyOn(activeDashboardFilters, 'buildActiveFilters').mockImplementation( + vi.fn(), + ); expect( dashboardFiltersReducer( dashboardFilters as unknown as Parameters< diff --git a/superset-frontend/src/dashboard/util/crossFilters.test.ts b/superset-frontend/src/dashboard/util/crossFilters.test.ts index d6519dcec64..53df0bc7cba 100644 --- a/superset-frontend/src/dashboard/util/crossFilters.test.ts +++ b/superset-frontend/src/dashboard/util/crossFilters.test.ts @@ -17,6 +17,7 @@ * under the License. */ import { Behavior, getChartMetadataRegistry, VizType } from '@superset-ui/core'; +import { vi, type Mock } from 'vitest'; import { getCrossFiltersConfiguration } from './crossFilters'; import { DEFAULT_CROSS_FILTER_SCOPING } from '../constants'; @@ -126,12 +127,12 @@ const CHART_CONFIG_METADATA = { global_chart_configuration: GLOBAL_CHART_CONFIG, }; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getChartMetadataRegistry: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getChartMetadataRegistry: vi.fn(), })); -const mockedGetChartMetadataRegistry = getChartMetadataRegistry as jest.Mock; +const mockedGetChartMetadataRegistry = getChartMetadataRegistry as Mock; beforeEach(() => { mockedGetChartMetadataRegistry.mockImplementation(() => ({ diff --git a/superset-frontend/src/dashboard/util/getDashboardUrl.test.ts b/superset-frontend/src/dashboard/util/getDashboardUrl.test.ts index c68caafe2e1..f29a5b26a34 100644 --- a/superset-frontend/src/dashboard/util/getDashboardUrl.test.ts +++ b/superset-frontend/src/dashboard/util/getDashboardUrl.test.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { vi } from 'vitest'; import getDashboardUrl from 'src/dashboard/util/getDashboardUrl'; import { DASHBOARD_FILTER_SCOPE_GLOBAL } from 'src/dashboard/reducers/dashboardFilters'; import { DashboardStandaloneMode } from 'src/dashboard/util/constants'; @@ -31,6 +32,7 @@ describe('getChartIdsFromLayout', () => { const globalLocation = window.location; afterEach(() => { + // @ts-ignore window.location = globalLocation; }); @@ -93,7 +95,7 @@ describe('getChartIdsFromLayout', () => { }); test('should preserve unknown filters', () => { - const windowSpy = jest.spyOn(window, 'window', 'get'); + const windowSpy = vi.spyOn(window, 'window', 'get'); windowSpy.mockImplementation( () => ({ @@ -116,7 +118,7 @@ describe('getChartIdsFromLayout', () => { }); test('should process native filters key', () => { - const windowSpy = jest.spyOn(window, 'window', 'get'); + const windowSpy = vi.spyOn(window, 'window', 'get'); windowSpy.mockImplementation( () => ({ diff --git a/superset-frontend/src/dashboard/util/logging/childChartsDidLoad.test.ts b/superset-frontend/src/dashboard/util/logging/childChartsDidLoad.test.ts index 1f6b898a44a..d2670c2cdea 100644 --- a/superset-frontend/src/dashboard/util/logging/childChartsDidLoad.test.ts +++ b/superset-frontend/src/dashboard/util/logging/childChartsDidLoad.test.ts @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +import { vi, type Mock } from 'vitest'; import { ChartState } from 'src/explore/types'; import { Layout } from 'src/dashboard/types'; import childChartsDidLoad from './childChartsDidLoad'; @@ -23,20 +24,18 @@ import childChartsDidLoad from './childChartsDidLoad'; import mockFindNonTabChildChartIdsImport from './findNonTabChildChartIds'; // Mock the findNonTabChildChartIds dependency -jest.mock('./findNonTabChildChartIds', () => ({ - __esModule: true, - default: jest.fn(), +vi.mock('./findNonTabChildChartIds', () => ({ + default: vi.fn(), })); -const mockFindNonTabChildChartIds = - mockFindNonTabChildChartIdsImport as jest.MockedFunction< - typeof mockFindNonTabChildChartIdsImport - >; +const mockFindNonTabChildChartIds = mockFindNonTabChildChartIdsImport as Mock< + typeof mockFindNonTabChildChartIdsImport +>; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('childChartsDidLoad', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('returns didLoad true when all charts are in completed states', () => { diff --git a/superset-frontend/src/dashboard/util/permissionUtils.test.ts b/superset-frontend/src/dashboard/util/permissionUtils.test.ts index e7d9d133900..c1b3908f1c6 100644 --- a/superset-frontend/src/dashboard/util/permissionUtils.test.ts +++ b/superset-frontend/src/dashboard/util/permissionUtils.test.ts @@ -17,6 +17,7 @@ * under the License. */ import { isFeatureEnabled, FeatureFlag } from '@superset-ui/core'; +import { vi, type Mock } from 'vitest'; import { UndefinedUser, UserWithPermissionsAndRoles, @@ -97,12 +98,12 @@ const dashboard: Dashboard = { roles: [], }; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - isFeatureEnabled: jest.fn(), +vi.mock('@superset-ui/core', async (importActual) => ({ + ...(await importActual()), + isFeatureEnabled: vi.fn(), })); -const mockedIsFeatureEnabled = isFeatureEnabled as jest.Mock; +const mockedIsFeatureEnabled = isFeatureEnabled as Mock; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('canUserEditDashboard', () => { diff --git a/superset-frontend/src/embedded/utils.test.ts b/superset-frontend/src/embedded/utils.test.ts index a28f47d2527..1f294578f9f 100644 --- a/superset-frontend/src/embedded/utils.test.ts +++ b/superset-frontend/src/embedded/utils.test.ts @@ -24,11 +24,12 @@ import * as chartStateConverter from 'src/dashboard/util/chartStateConverter'; import * as exploreUtils from 'src/explore/exploreUtils'; import getFormDataWithExtraFilters from 'src/dashboard/util/charts/getFormDataWithExtraFilters'; import { getAppliedFilterValues } from 'src/dashboard/util/activeDashboardFilters'; +import { Mock } from 'vitest'; -jest.mock('src/dashboard/util/chartStateConverter'); -jest.mock('src/dashboard/util/charts/getFormDataWithExtraFilters'); -jest.mock('src/dashboard/util/activeDashboardFilters'); -jest.mock('src/explore/exploreUtils'); +vi.mock('src/dashboard/util/chartStateConverter'); +vi.mock('src/dashboard/util/charts/getFormDataWithExtraFilters'); +vi.mock('src/dashboard/util/activeDashboardFilters'); +vi.mock('src/explore/exploreUtils'); const dataMask: DataMaskStateWithId = { '1': { @@ -147,15 +148,15 @@ const mockState: Partial = { } as Partial; beforeEach(() => { - jest.clearAllMocks(); - (getFormDataWithExtraFilters as jest.Mock).mockImplementation( + vi.clearAllMocks(); + (getFormDataWithExtraFilters as Mock).mockImplementation( ({ chart }: any) => chart.form_data, ); - (getAppliedFilterValues as jest.Mock).mockReturnValue({}); + (getAppliedFilterValues as Mock).mockReturnValue({}); }); test('getChartDataPayloads returns empty object when charts with state converters are not found', async () => { - const mockHasChartStateConverter = jest + const mockHasChartStateConverter = vi .spyOn(chartStateConverter, 'hasChartStateConverter') .mockReturnValue(false); @@ -171,16 +172,15 @@ test('getChartDataPayloads generates payloads for charts with state converters', queries: [{ some: 'query' }], }; - jest - .spyOn(chartStateConverter, 'hasChartStateConverter') - .mockImplementation((vizType: string) => vizType === 'ag-grid-table'); + vi.spyOn(chartStateConverter, 'hasChartStateConverter').mockImplementation( + (vizType: string) => vizType === 'ag-grid-table', + ); - jest - .spyOn(chartStateConverter, 'convertChartStateToOwnState') - .mockReturnValue({ converted: 'state' }); + vi.spyOn(chartStateConverter, 'convertChartStateToOwnState').mockReturnValue({ + converted: 'state', + }); - jest - .spyOn(exploreUtils, 'buildV1ChartDataPayload') + vi.spyOn(exploreUtils, 'buildV1ChartDataPayload') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockResolvedValue(mockPayload as any); @@ -202,16 +202,13 @@ test('getChartDataPayloads filters by specific chartId when provided', async () queries: [{ some: 'query' }], }; - jest - .spyOn(chartStateConverter, 'hasChartStateConverter') - .mockReturnValue(true); + vi.spyOn(chartStateConverter, 'hasChartStateConverter').mockReturnValue(true); - jest - .spyOn(chartStateConverter, 'convertChartStateToOwnState') - .mockReturnValue({ converted: 'state' }); + vi.spyOn(chartStateConverter, 'convertChartStateToOwnState').mockReturnValue({ + converted: 'state', + }); - jest - .spyOn(exploreUtils, 'buildV1ChartDataPayload') + vi.spyOn(exploreUtils, 'buildV1ChartDataPayload') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockResolvedValue(mockPayload as any); @@ -225,9 +222,9 @@ test('getChartDataPayloads filters by specific chartId when provided', async () }); test('getChartDataPayloads returns error object for specific chartId not found', async () => { - jest - .spyOn(chartStateConverter, 'hasChartStateConverter') - .mockReturnValue(false); + vi.spyOn(chartStateConverter, 'hasChartStateConverter').mockReturnValue( + false, + ); const result = await getChartDataPayloads(mockState as RootState, { chartId: 999, @@ -246,16 +243,13 @@ test('getChartDataPayloads handles errors during payload generation gracefully', queries: [{ some: 'query' }], }; - jest - .spyOn(chartStateConverter, 'hasChartStateConverter') - .mockReturnValue(true); + vi.spyOn(chartStateConverter, 'hasChartStateConverter').mockReturnValue(true); - jest - .spyOn(chartStateConverter, 'convertChartStateToOwnState') - .mockReturnValue({ converted: 'state' }); + vi.spyOn(chartStateConverter, 'convertChartStateToOwnState').mockReturnValue({ + converted: 'state', + }); - jest - .spyOn(exploreUtils, 'buildV1ChartDataPayload') + vi.spyOn(exploreUtils, 'buildV1ChartDataPayload') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockImplementation((params: any) => { if (params.formData.viz_type === 'ag-grid-table') { @@ -280,15 +274,13 @@ test('getChartDataPayloads merges baseOwnState with converted chart state', asyn queries: [{ some: 'query' }], }; - jest - .spyOn(chartStateConverter, 'hasChartStateConverter') - .mockReturnValue(true); + vi.spyOn(chartStateConverter, 'hasChartStateConverter').mockReturnValue(true); - jest - .spyOn(chartStateConverter, 'convertChartStateToOwnState') - .mockReturnValue({ converted: 'state' }); + vi.spyOn(chartStateConverter, 'convertChartStateToOwnState').mockReturnValue({ + converted: 'state', + }); - const mockBuildPayload = jest + const mockBuildPayload = vi .spyOn(exploreUtils, 'buildV1ChartDataPayload') // eslint-disable-next-line @typescript-eslint/no-explicit-any .mockResolvedValue(mockPayload as any); diff --git a/superset-frontend/src/explore/actions/datasourcesActions.test.ts b/superset-frontend/src/explore/actions/datasourcesActions.test.ts index c82b6e758c7..cf9657b901f 100644 --- a/superset-frontend/src/explore/actions/datasourcesActions.test.ts +++ b/superset-frontend/src/explore/actions/datasourcesActions.test.ts @@ -26,12 +26,12 @@ import { import datasourcesReducer from '../reducers/datasourcesReducer'; import { updateFormDataByDatasource } from './exploreActions'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getClientErrorObject: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getClientErrorObject: vi.fn(), })); -const mockedGetClientErrorObject = getClientErrorObject as jest.Mock; +const mockedGetClientErrorObject = getClientErrorObject as vi.Mock; const CURRENT_DATASOURCE = { id: 1, @@ -87,8 +87,8 @@ test('sets new datasource', () => { }); test('change datasource action', () => { - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ explore: { datasource: CURRENT_DATASOURCE, }, @@ -111,8 +111,8 @@ test('saveDataset handles success', async () => { }; fetchMock.clearHistory().removeRoutes(); fetchMock.post(saveDatasetEndpoint, saveDatasetResponse); - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ explore: { datasource } })); + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ explore: { datasource } })); const dataset = await saveDataset(SAVE_DATASET_POST_ARGS)(dispatch); expect(fetchMock.callHistory.calls(saveDatasetEndpoint)).toHaveLength(1); @@ -131,7 +131,7 @@ test('updateSlice with add to existing dashboard handles failure', async () => { Promise.resolve(sampleError), ); fetchMock.post(saveDatasetEndpoint, { throws: sampleError }); - const dispatch = jest.fn(); + const dispatch = vi.fn(); let caughtError; try { diff --git a/superset-frontend/src/explore/actions/hydrateExplore.test.ts b/superset-frontend/src/explore/actions/hydrateExplore.test.ts index 31afeb01c9d..166755fe071 100644 --- a/superset-frontend/src/explore/actions/hydrateExplore.test.ts +++ b/superset-frontend/src/explore/actions/hydrateExplore.test.ts @@ -22,8 +22,8 @@ import { hydrateExplore, HYDRATE_EXPLORE } from './hydrateExplore'; import { exploreInitialData } from '../fixtures'; test('creates hydrate action from initial data', () => { - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ user: {}, charts: {}, datasources: {}, @@ -95,8 +95,8 @@ test('creates hydrate action from initial data', () => { }); test('creates hydrate action with existing state', () => { - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ user: {}, charts: {}, datasources: {}, @@ -169,8 +169,8 @@ test('creates hydrate action with existing state', () => { }); test('uses configured default time range if not set', () => { - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ user: {}, charts: {}, datasources: {}, @@ -215,8 +215,8 @@ test('uses configured default time range if not set', () => { }); test('extracts currency formats from metrics in dataset', () => { - const dispatch = jest.fn(); - const getState = jest.fn(() => ({ + const dispatch = vi.fn(); + const getState = vi.fn(() => ({ user: {}, charts: {}, datasources: {}, diff --git a/superset-frontend/src/explore/actions/saveModalActions.test.ts b/superset-frontend/src/explore/actions/saveModalActions.test.ts index 9a6b667079c..7e8313f5c43 100644 --- a/superset-frontend/src/explore/actions/saveModalActions.test.ts +++ b/superset-frontend/src/explore/actions/saveModalActions.test.ts @@ -93,8 +93,8 @@ const sliceResponsePayload: Partial = { const sampleError = new Error('sampleError'); -jest.mock('../exploreUtils', () => ({ - buildV1ChartDataPayload: jest.fn(() => queryContext), +vi.mock('../exploreUtils', () => ({ + buildV1ChartDataPayload: vi.fn(() => queryContext), })); beforeEach(() => fetchMock.clearHistory().removeRoutes()); @@ -107,7 +107,7 @@ test('updateSlice handles success', async () => { fetchMock.put(updateSliceEndpoint, sliceResponsePayload, { name: updateSliceEndpoint, }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => { dispatchSpy(action); }; @@ -156,7 +156,7 @@ test('updateSlice handles failure', async () => { { name: updateSliceEndpoint }, ); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => { dispatchSpy(action); }; @@ -208,7 +208,7 @@ test('createSlice handles success', async () => { fetchMock.post(createSliceEndpoint, sliceResponsePayload, { name: createSliceEndpoint, }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => dispatchSpy(action); const getState = () => mockExploreState; const slice: Partial = await createSlice(sliceName, [])( @@ -230,7 +230,7 @@ test('createSlice handles success', async () => { test('createSlice handles failure', async () => { fetchMock.post(createSliceEndpoint, { throws: sampleError }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => dispatchSpy(action); const getState = () => mockExploreState; @@ -261,7 +261,7 @@ test('createDashboard handles success', async () => { fetchMock.post(createDashboardEndpoint, dashboardResponsePayload, { name: createDashboardEndpoint, }); - const dispatch = jest.fn(); + const dispatch = vi.fn(); const dashboard = await createDashboard(dashboardName)( dispatch as Dispatch, ); @@ -276,7 +276,7 @@ test('createDashboard handles failure', async () => { { throws: sampleError }, { name: createDashboardEndpoint }, ); - const dispatch = jest.fn(); + const dispatch = vi.fn(); let caughtError; try { await createDashboard(dashboardName)(dispatch as Dispatch); @@ -294,7 +294,7 @@ test('updateSlice with add to new dashboard handles success', async () => { fetchMock.put(updateSliceEndpoint, sliceResponsePayload, { name: updateSliceEndpoint, }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => dispatchSpy(action); const getState = () => mockExploreState; @@ -354,7 +354,7 @@ test('updateSlice with add to existing dashboard handles success', async () => { fetchMock.put(updateSliceEndpoint, sliceResponsePayload, { name: updateSliceEndpoint, }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => dispatchSpy(action); const getState = () => mockExploreState; const slice = await updateSlice( @@ -424,7 +424,7 @@ test('getSliceDashboards with slice handles success', async () => { fetchMock.get(getSliceDashboardsEndpoint, dashboardSlicesResponsePayload, { name: getSliceDashboardsEndpoint, }); - const dispatchSpy = jest.fn(); + const dispatchSpy = vi.fn(); const dispatch = (action: any) => dispatchSpy(action); const sliceDashboards = await getSliceDashboards({ slice_id: 10, @@ -449,7 +449,7 @@ test('getSliceDashboards with slice handles failure', async () => { { throws: sampleError }, { name: getSliceDashboardsEndpoint }, ); - const dispatch = jest.fn(); + const dispatch = vi.fn(); let caughtError; try { await getSliceDashboards({ diff --git a/superset-frontend/src/explore/components/controls/FilterControl/utils/useDatePickerInAdhocfilter.test.ts b/superset-frontend/src/explore/components/controls/FilterControl/utils/useDatePickerInAdhocfilter.test.ts index 27dffc2daff..13a737b01a6 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/utils/useDatePickerInAdhocfilter.test.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/utils/useDatePickerInAdhocfilter.test.ts @@ -32,7 +32,7 @@ test('should return undefined if column is not temporal', async () => { useDatePickerInAdhocFilter({ columnName: 'gender', datasource: TestDataset, - onChange: jest.fn(), + onChange: vi.fn(), }), ); expect(result.current).toBeUndefined(); @@ -44,7 +44,7 @@ test('should return JSX', async () => { useDatePickerInAdhocFilter({ columnName: 'ds', datasource: TestDataset, - onChange: jest.fn(), + onChange: vi.fn(), }), ); expect(result.current).not.toBeUndefined(); diff --git a/superset-frontend/src/explore/components/controls/FilterControl/utils/useGetTimeRangeLabel.test.ts b/superset-frontend/src/explore/components/controls/FilterControl/utils/useGetTimeRangeLabel.test.ts index 6aab9287b66..a91c5c6be4d 100644 --- a/superset-frontend/src/explore/components/controls/FilterControl/utils/useGetTimeRangeLabel.test.ts +++ b/superset-frontend/src/explore/components/controls/FilterControl/utils/useGetTimeRangeLabel.test.ts @@ -23,12 +23,12 @@ import { useGetTimeRangeLabel } from './useGetTimeRangeLabel'; import AdhocFilter from '../AdhocFilter'; import { Clauses, ExpressionTypes } from '../types'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - fetchTimeRange: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + fetchTimeRange: vi.fn(), })); -const mockedFetchTimeRange = fetchTimeRange as jest.Mock; +const mockedFetchTimeRange = fetchTimeRange as vi.Mock; test('should return empty object if operator is not TEMPORAL_RANGE', () => { const adhocFilter = new AdhocFilter({ diff --git a/superset-frontend/src/explore/exploreUtils/exportChart.test.ts b/superset-frontend/src/explore/exploreUtils/exportChart.test.ts index 661e1248b3e..8b83b409139 100644 --- a/superset-frontend/src/explore/exploreUtils/exportChart.test.ts +++ b/superset-frontend/src/explore/exploreUtils/exportChart.test.ts @@ -18,30 +18,32 @@ */ import { exportChart } from '.'; +const { ensureAppRootMock, getChartMetadataRegistryMock } = vi.hoisted(() => ({ + ensureAppRootMock: vi.fn((path: string) => path), + getChartMetadataRegistryMock: vi.fn().mockReturnValue({ + get: vi.fn().mockReturnValue(() => () => ({})), + }), +})); + // Mock pathUtils to control app root prefix -jest.mock('src/utils/pathUtils', () => ({ - ensureAppRoot: jest.fn((path: string) => path), +vi.mock('src/utils/pathUtils', () => ({ + ensureAppRoot: ensureAppRootMock, })); // Mock SupersetClient -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), SupersetClient: { - postForm: jest.fn(), - get: jest.fn().mockResolvedValue({ json: {} }), - post: jest.fn().mockResolvedValue({ json: {} }), + postForm: vi.fn(), + get: vi.fn().mockResolvedValue({ json: {} }), + post: vi.fn().mockResolvedValue({ json: {} }), }, - getChartBuildQueryRegistry: jest.fn().mockReturnValue({ - get: jest.fn().mockReturnValue(() => () => ({})), - }), - getChartMetadataRegistry: jest.fn().mockReturnValue({ - get: jest.fn().mockReturnValue({ parseMethod: 'json' }), + getChartBuildQueryRegistry: vi.fn().mockReturnValue({ + get: vi.fn().mockReturnValue(() => () => ({})), }), + getChartMetadataRegistry: getChartMetadataRegistryMock, })); -const { ensureAppRoot } = jest.requireMock('src/utils/pathUtils'); -const { getChartMetadataRegistry } = jest.requireMock('@superset-ui/core'); - // Minimal formData that won't trigger legacy API (useLegacyApi = false) const baseFormData = { datasource: '1__table', @@ -49,21 +51,21 @@ const baseFormData = { }; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Default: no prefix - ensureAppRoot.mockImplementation((path: string) => path); + ensureAppRootMock.mockImplementation((path: string) => path); // Default: v1 API (not legacy) - getChartMetadataRegistry.mockReturnValue({ - get: jest.fn().mockReturnValue({ parseMethod: 'json' }), + getChartMetadataRegistryMock.mockReturnValue({ + get: vi.fn().mockReturnValue({ parseMethod: 'json' }), }); }); // Tests for exportChart URL prefix handling in streaming export test('exportChart v1 API passes prefixed URL to onStartStreamingExport when app root is configured', async () => { const appRoot = '/superset'; - ensureAppRoot.mockImplementation((path: string) => `${appRoot}${path}`); + ensureAppRootMock.mockImplementation((path: string) => `${appRoot}${path}`); - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); await exportChart({ formData: baseFormData, @@ -78,9 +80,9 @@ test('exportChart v1 API passes prefixed URL to onStartStreamingExport when app }); test('exportChart v1 API passes unprefixed URL when no app root is configured', async () => { - ensureAppRoot.mockImplementation((path: string) => path); + ensureAppRootMock.mockImplementation((path: string) => path); - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); await exportChart({ formData: baseFormData, @@ -95,9 +97,9 @@ test('exportChart v1 API passes unprefixed URL when no app root is configured', test('exportChart v1 API passes nested prefix for deeply nested deployments', async () => { const appRoot = '/my-company/analytics/superset'; - ensureAppRoot.mockImplementation((path: string) => `${appRoot}${path}`); + ensureAppRootMock.mockImplementation((path: string) => `${appRoot}${path}`); - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); await exportChart({ formData: baseFormData, @@ -112,7 +114,7 @@ test('exportChart v1 API passes nested prefix for deeply nested deployments', as }); test('exportChart passes csv exportType for CSV exports', async () => { - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); await exportChart({ formData: baseFormData, @@ -128,7 +130,7 @@ test('exportChart passes csv exportType for CSV exports', async () => { }); test('exportChart passes xlsx exportType for Excel exports', async () => { - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); await exportChart({ formData: baseFormData, @@ -146,14 +148,14 @@ test('exportChart passes xlsx exportType for Excel exports', async () => { test('exportChart legacy API (useLegacyApi=true) passes prefixed URL with app root configured', async () => { // Legacy API uses getExploreUrl() -> getURIDirectory() -> ensureAppRoot() const appRoot = '/superset'; - ensureAppRoot.mockImplementation((path: string) => `${appRoot}${path}`); + ensureAppRootMock.mockImplementation((path: string) => `${appRoot}${path}`); // Configure mock to return useLegacyApi: true - getChartMetadataRegistry.mockReturnValue({ - get: jest.fn().mockReturnValue({ useLegacyApi: true, parseMethod: 'json' }), + getChartMetadataRegistryMock.mockReturnValue({ + get: vi.fn().mockReturnValue({ useLegacyApi: true, parseMethod: 'json' }), }); - const onStartStreamingExport = jest.fn(); + const onStartStreamingExport = vi.fn(); const legacyFormData = { datasource: '1__table', viz_type: 'legacy_viz', diff --git a/superset-frontend/src/explore/exploreUtils/formData.test.ts b/superset-frontend/src/explore/exploreUtils/formData.test.ts index c61b4370377..38981eb112c 100644 --- a/superset-frontend/src/explore/exploreUtils/formData.test.ts +++ b/superset-frontend/src/explore/exploreUtils/formData.test.ts @@ -19,10 +19,10 @@ import { SupersetClient } from '@superset-ui/core'; import { postFormData, putFormData } from './formData'; -jest.mock('@superset-ui/core', () => ({ +vi.mock('@superset-ui/core', () => ({ SupersetClient: { - post: jest.fn(), - put: jest.fn(), + post: vi.fn(), + put: vi.fn(), }, })); @@ -30,7 +30,7 @@ test('postFormData should call SupersetClient.post with correct payload and retu const mockKey = '123abc'; const mockResponse = { json: { key: mockKey } }; - (SupersetClient.post as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.post as vi.Mock).mockResolvedValue(mockResponse); const result = await postFormData( 1, @@ -57,7 +57,7 @@ test('putFormData should call SupersetClient.put with correct payload and return const mockMessage = 'Form data updated'; const mockResponse = { json: { message: mockMessage } }; - (SupersetClient.put as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.put as vi.Mock).mockResolvedValue(mockResponse); const result = await putFormData( 10, @@ -81,7 +81,7 @@ test('putFormData should call SupersetClient.put with correct payload and return }); test('postFormData without optional params should work', async () => { - (SupersetClient.post as jest.Mock).mockResolvedValue({ + (SupersetClient.post as vi.Mock).mockResolvedValue({ json: { key: 'abc' }, }); diff --git a/superset-frontend/src/explore/exploreUtils/getChartDataUri.test.ts b/superset-frontend/src/explore/exploreUtils/getChartDataUri.test.ts index 8745e27b131..1f79dfe85bb 100644 --- a/superset-frontend/src/explore/exploreUtils/getChartDataUri.test.ts +++ b/superset-frontend/src/explore/exploreUtils/getChartDataUri.test.ts @@ -18,66 +18,62 @@ */ import { ensureAppRoot } from 'src/utils/pathUtils'; import { getChartDataUri } from '.'; +import { Mock } from 'vitest'; -jest.mock('src/utils/pathUtils'); +vi.mock('src/utils/pathUtils'); -// eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks -describe('Get ChartUri', () => { - (ensureAppRoot as jest.Mock).mockImplementation( - (path: string) => `/prefix${path}`, - ); +(ensureAppRoot as Mock).mockImplementation((path: string) => `/prefix${path}`); - test('Get ChartUri when allowDomainSharding:false', () => { - expect( - getChartDataUri({ - path: '/path', - qs: { key: 'same-string' }, - allowDomainSharding: false, - }), - ).toEqual({ - _deferred_build: true, - _parts: { - duplicateQueryParameters: false, - escapeQuerySpace: true, - fragment: null, - hostname: 'localhost', - password: null, - path: '/prefix/path', - port: '', - preventInvalidHostname: false, - protocol: 'http', - query: 'key=same-string', - urn: null, - username: null, - }, - _string: '', - }); - }); - - test('Get ChartUri when allowDomainSharding:true', () => { - expect( - getChartDataUri({ - path: '/path-allowDomainSharding-true', - qs: { key: 'allowDomainSharding-true' }, - allowDomainSharding: true, - }), - ).toEqual({ - _deferred_build: true, - _parts: { - duplicateQueryParameters: false, - escapeQuerySpace: true, - fragment: null, - hostname: undefined, - password: null, - path: '/prefix/path-allowDomainSharding-true', - port: '', - preventInvalidHostname: false, - protocol: 'http', - query: 'key=allowDomainSharding-true', - urn: null, - username: null, - }, - _string: '', - }); +test('Get ChartUri when allowDomainSharding:false', () => { + expect( + getChartDataUri({ + path: '/path', + qs: { key: 'same-string' }, + allowDomainSharding: false, + }), + ).toEqual({ + _deferred_build: true, + _parts: { + duplicateQueryParameters: false, + escapeQuerySpace: true, + fragment: null, + hostname: 'localhost', + password: null, + path: '/prefix/path', + port: '', + preventInvalidHostname: false, + protocol: 'http', + query: 'key=same-string', + urn: null, + username: null, + }, + _string: '', + }); +}); + +test('Get ChartUri when allowDomainSharding:true', () => { + expect( + getChartDataUri({ + path: '/path-allowDomainSharding-true', + qs: { key: 'allowDomainSharding-true' }, + allowDomainSharding: true, + }), + ).toEqual({ + _deferred_build: true, + _parts: { + duplicateQueryParameters: false, + escapeQuerySpace: true, + fragment: null, + hostname: undefined, + password: null, + path: '/prefix/path-allowDomainSharding-true', + port: '', + preventInvalidHostname: false, + protocol: 'http', + query: 'key=allowDomainSharding-true', + urn: null, + username: null, + }, + _string: '', }); }); diff --git a/superset-frontend/src/explore/exploreUtils/getHostName.test.ts b/superset-frontend/src/explore/exploreUtils/getHostName.test.ts index 6711eca4a2f..dc255841907 100644 --- a/superset-frontend/src/explore/exploreUtils/getHostName.test.ts +++ b/superset-frontend/src/explore/exploreUtils/getHostName.test.ts @@ -18,7 +18,7 @@ */ import { getHostName } from '.'; -jest.mock('src/utils/hostNamesConfig', () => ({ +vi.mock('src/utils/hostNamesConfig', () => ({ availableDomains: [ 'domain-a', 'domain-b', diff --git a/superset-frontend/src/explore/exploreUtils/shouldUseLegacyApi.test.ts b/superset-frontend/src/explore/exploreUtils/shouldUseLegacyApi.test.ts index caeeae68787..ba958395c8e 100644 --- a/superset-frontend/src/explore/exploreUtils/shouldUseLegacyApi.test.ts +++ b/superset-frontend/src/explore/exploreUtils/shouldUseLegacyApi.test.ts @@ -19,15 +19,15 @@ import { getChartMetadataRegistry } from '@superset-ui/core'; import { getQuerySettings } from '.'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - getChartMetadataRegistry: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + getChartMetadataRegistry: vi.fn(), })); -const mockedGetChartMetadataRegistry = getChartMetadataRegistry as jest.Mock; +const mockedGetChartMetadataRegistry = getChartMetadataRegistry as vi.Mock; test('Should return false', () => { - const get = jest.fn(); + const get = vi.fn(); mockedGetChartMetadataRegistry.mockReturnValue({ get } as any); expect(get).toHaveBeenCalledTimes(0); const [useLegacyApi] = getQuerySettings({ viz_type: 'name_test' }); @@ -37,7 +37,7 @@ test('Should return false', () => { }); test('Should return true', () => { - const get = jest.fn(); + const get = vi.fn(); get.mockReturnValue({ useLegacyApi: true }); mockedGetChartMetadataRegistry.mockReturnValue({ get } as any); expect(get).toHaveBeenCalledTimes(0); @@ -48,7 +48,7 @@ test('Should return true', () => { }); test('Should return false when useLegacyApi:false', () => { - const get = jest.fn(); + const get = vi.fn(); get.mockReturnValue({ useLegacyApi: false }); mockedGetChartMetadataRegistry.mockReturnValue({ get } as any); expect(get).toHaveBeenCalledTimes(0); diff --git a/superset-frontend/src/extensions/ExtensionsContextUtils.test.ts b/superset-frontend/src/extensions/ExtensionsContextUtils.test.ts index ecd6b7950b0..0599619a861 100644 --- a/superset-frontend/src/extensions/ExtensionsContextUtils.test.ts +++ b/superset-frontend/src/extensions/ExtensionsContextUtils.test.ts @@ -23,9 +23,9 @@ import { import { ExtensionsContextType } from './ExtensionsContext'; const mockExtensionsContext: ExtensionsContextType = { - getView: jest.fn(), - registerViewProvider: jest.fn(), - unregisterViewProvider: jest.fn(), + getView: vi.fn(), + registerViewProvider: vi.fn(), + unregisterViewProvider: vi.fn(), }; test('sets and gets extensions context value', () => { @@ -54,15 +54,15 @@ test('throws error when getting context value before setting it', () => { test('overwrites previous context value when setting new one', () => { const firstContext: ExtensionsContextType = { - getView: jest.fn().mockReturnValue('first-view'), - registerViewProvider: jest.fn(), - unregisterViewProvider: jest.fn(), + getView: vi.fn().mockReturnValue('first-view'), + registerViewProvider: vi.fn(), + unregisterViewProvider: vi.fn(), }; const secondContext: ExtensionsContextType = { - getView: jest.fn().mockReturnValue('second-view'), - registerViewProvider: jest.fn(), - unregisterViewProvider: jest.fn(), + getView: vi.fn().mockReturnValue('second-view'), + registerViewProvider: vi.fn(), + unregisterViewProvider: vi.fn(), }; setExtensionsContextValue(firstContext); diff --git a/superset-frontend/src/extensions/ExtensionsManager.test.ts b/superset-frontend/src/extensions/ExtensionsManager.test.ts index b98e2dc08ee..41d7b339edb 100644 --- a/superset-frontend/src/extensions/ExtensionsManager.test.ts +++ b/superset-frontend/src/extensions/ExtensionsManager.test.ts @@ -71,8 +71,8 @@ function createMockExtension( menus, views, }, - activate: includeMockFunctions ? jest.fn() : undefined!, - deactivate: includeMockFunctions ? jest.fn() : undefined!, + activate: includeMockFunctions ? vi.fn() : undefined!, + deactivate: includeMockFunctions ? vi.fn() : undefined!, }; return extension; @@ -404,7 +404,7 @@ test('deactivateExtension successfully deactivates an enabled extension', async test('deactivateExtension disposes of context disposables', async () => { const manager = ExtensionsManager.getInstance(); - const mockDisposable = { dispose: jest.fn() }; + const mockDisposable = { dispose: vi.fn() }; await createActivatedExtension( manager, @@ -462,7 +462,7 @@ test('deactivateExtension handles errors during deactivation gracefully', async const mockExtension = await createActivatedExtension(manager); // Override the deactivate function to throw an error - mockExtension.deactivate = jest.fn(() => { + mockExtension.deactivate = vi.fn(() => { throw new Error('Deactivation error'); }); @@ -476,7 +476,7 @@ test('deactivateExtension handles errors during deactivation gracefully', async test('deactivateExtension handles errors during dispose gracefully', async () => { const manager = ExtensionsManager.getInstance(); const mockDisposable = { - dispose: jest.fn(() => { + dispose: vi.fn(() => { throw new Error('Dispose error'); }), }; diff --git a/superset-frontend/src/features/datasets/hooks/useDatasetLists.test.ts b/superset-frontend/src/features/datasets/hooks/useDatasetLists.test.ts index 8ff86e95034..71dd58c6d18 100644 --- a/superset-frontend/src/features/datasets/hooks/useDatasetLists.test.ts +++ b/superset-frontend/src/features/datasets/hooks/useDatasetLists.test.ts @@ -22,8 +22,8 @@ import { SupersetClient, JsonResponse } from '@superset-ui/core'; import rison from 'rison'; import useDatasetsList from './useDatasetLists'; -const mockAddDangerToast = jest.fn(); -jest.mock('src/components/MessageToasts/actions', () => ({ +const mockAddDangerToast = vi.fn(); +vi.mock('src/components/MessageToasts/actions', () => ({ addDangerToast: (msg: string) => mockAddDangerToast(msg), })); @@ -40,15 +40,15 @@ const mockDatasets = [ ]; beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); test('useDatasetsList fetches first page of datasets successfully', async () => { - const getSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ + const getSpy = vi.spyOn(SupersetClient, 'get').mockResolvedValue({ json: { count: 2, result: mockDatasets, @@ -72,7 +72,7 @@ test('useDatasetsList fetches multiple pages (pagination) until count reached', ]; const page2Data = [{ id: 3, table_name: 'table3', schema: 'public' }]; - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockResolvedValueOnce({ json: { @@ -98,7 +98,7 @@ test('useDatasetsList fetches multiple pages (pagination) until count reached', }); test('useDatasetsList extracts dataset names correctly', async () => { - const getSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ + const getSpy = vi.spyOn(SupersetClient, 'get').mockResolvedValue({ json: { count: 3, result: [ @@ -124,7 +124,7 @@ test('useDatasetsList extracts dataset names correctly', async () => { test('useDatasetsList handles API 500 error gracefully', async () => { // Mock error - loop should break immediately - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockRejectedValue(new Error('Internal Server Error')); @@ -143,7 +143,7 @@ test('useDatasetsList handles API 500 error gracefully', async () => { }); test('useDatasetsList handles empty dataset response', async () => { - const getSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ + const getSpy = vi.spyOn(SupersetClient, 'get').mockResolvedValue({ json: { count: 0, result: [], @@ -162,7 +162,7 @@ test('useDatasetsList handles empty dataset response', async () => { test('useDatasetsList stops pagination when results reach count', async () => { // First page returns 2 items, second page returns empty (no more results) - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockResolvedValueOnce({ json: { @@ -192,7 +192,7 @@ test('useDatasetsList stops pagination when results reach count', async () => { }); test('useDatasetsList resets datasets when schema changes', async () => { - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockResolvedValueOnce({ json: { @@ -242,7 +242,7 @@ test('useDatasetsList handles network timeout gracefully', async () => { }; timeoutError.status = 0; - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockRejectedValue(timeoutError); @@ -262,7 +262,7 @@ test('useDatasetsList handles network timeout gracefully', async () => { test('useDatasetsList breaks pagination loop on persistent API errors', async () => { // Mock API that always fails (persistent error) - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockRejectedValue(new Error('Persistent server error')); @@ -283,7 +283,7 @@ test('useDatasetsList breaks pagination loop on persistent API errors', async () test('useDatasetsList handles error on second page gracefully', async () => { // First page succeeds, second page fails - const getSpy = jest + const getSpy = vi .spyOn(SupersetClient, 'get') .mockResolvedValueOnce({ json: { @@ -310,7 +310,7 @@ test('useDatasetsList handles error on second page gracefully', async () => { }); test('useDatasetsList skips fetching when schema is null or undefined', () => { - const getSpy = jest.spyOn(SupersetClient, 'get'); + const getSpy = vi.spyOn(SupersetClient, 'get'); // Test with null schema const { result: resultNull, rerender } = renderHook( @@ -331,7 +331,7 @@ test('useDatasetsList skips fetching when schema is null or undefined', () => { }); test('useDatasetsList skips fetching when db is undefined', () => { - const getSpy = jest.spyOn(SupersetClient, 'get'); + const getSpy = vi.spyOn(SupersetClient, 'get'); const { result } = renderHook(() => useDatasetsList(undefined, 'public')); @@ -342,7 +342,7 @@ test('useDatasetsList skips fetching when db is undefined', () => { }); test('useDatasetsList skips fetching when db.id is undefined', () => { - const getSpy = jest.spyOn(SupersetClient, 'get'); + const getSpy = vi.spyOn(SupersetClient, 'get'); // Create db object without id property const dbWithoutId = { @@ -359,7 +359,7 @@ test('useDatasetsList skips fetching when db.id is undefined', () => { }); test('useDatasetsList encodes schemas with spaces and special characters in endpoint URL', async () => { - const getSpy = jest.spyOn(SupersetClient, 'get').mockResolvedValue({ + const getSpy = vi.spyOn(SupersetClient, 'get').mockResolvedValue({ json: { count: 0, result: [] }, } as unknown as JsonResponse); diff --git a/superset-frontend/src/hooks/apiResources/apiResources.test.ts b/superset-frontend/src/hooks/apiResources/apiResources.test.ts index 89a7759cc99..2aa1d992034 100644 --- a/superset-frontend/src/hooks/apiResources/apiResources.test.ts +++ b/superset-frontend/src/hooks/apiResources/apiResources.test.ts @@ -25,31 +25,31 @@ import { useTransformedResource, } from './apiResources'; -const fakeApiResult = { - id: 1, - name: 'fake api result', -}; +const { fakeApiResult } = vi.hoisted(() => ({ + fakeApiResult: { + id: 1, + name: 'fake api result', + }, +})); const nameToAllCaps = (thing: any) => ({ ...thing, name: thing.name.toUpperCase(), }); -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - makeApi: jest - .fn() - .mockReturnValue(jest.fn().mockResolvedValue(fakeApiResult)), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + makeApi: vi.fn().mockReturnValue(vi.fn().mockResolvedValue(fakeApiResult)), })); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('apiResource hooks', () => { beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks @@ -64,7 +64,7 @@ describe('apiResource hooks', () => { error: null, }); await act(async () => { - jest.runAllTimers(); + vi.runAllTimers(); }); }); @@ -73,7 +73,7 @@ describe('apiResource hooks', () => { useApiResourceFullBody('/test/endpoint'), ); await act(async () => { - jest.runAllTimers(); + vi.runAllTimers(); }); expect(result.current).toEqual({ status: ResourceStatus.Complete, @@ -84,12 +84,12 @@ describe('apiResource hooks', () => { test('handles api errors', async () => { const fakeError = new Error('fake api error'); - (makeApi as any).mockReturnValue(jest.fn().mockRejectedValue(fakeError)); + (makeApi as any).mockReturnValue(vi.fn().mockRejectedValue(fakeError)); const { result } = renderHook(() => useApiResourceFullBody('/test/endpoint'), ); await act(async () => { - jest.runAllTimers(); + vi.runAllTimers(); }); expect(result.current).toEqual({ status: ResourceStatus.Error, @@ -149,7 +149,7 @@ describe('apiResource hooks', () => { describe('useApiV1Endpoint', () => { test('resolves to the value from the api', async () => { (makeApi as any).mockReturnValue( - jest.fn().mockResolvedValue({ + vi.fn().mockResolvedValue({ meta: 'data', count: 1, result: fakeApiResult, @@ -157,7 +157,7 @@ describe('apiResource hooks', () => { ); const { result } = renderHook(() => useApiV1Resource('/test/endpoint')); await act(async () => { - jest.runAllTimers(); + vi.runAllTimers(); }); expect(result.current).toEqual({ status: ResourceStatus.Complete, diff --git a/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts b/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts index 70069a00478..77a6a7923dd 100644 --- a/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts +++ b/superset-frontend/src/hooks/apiResources/databaseFunctions.test.ts @@ -72,6 +72,7 @@ test('returns api response mapping json result', async () => { }); test('returns cached data without api request', async () => { + fetchMock.clearHistory(); const { result, waitFor, rerender } = renderHook( () => useDatabaseFunctionsQuery({ diff --git a/superset-frontend/src/hooks/apiResources/datasets.test.ts b/superset-frontend/src/hooks/apiResources/datasets.test.ts index e60b5f33742..4d8dc8b6e5f 100644 --- a/superset-frontend/src/hooks/apiResources/datasets.test.ts +++ b/superset-frontend/src/hooks/apiResources/datasets.test.ts @@ -28,35 +28,35 @@ import { useDatasetDrillInfo, } from './datasets'; -jest.mock('src/utils/cachedSupersetGet', () => ({ - cachedSupersetGet: jest.fn(), +vi.mock('src/utils/cachedSupersetGet', () => ({ + cachedSupersetGet: vi.fn(), supersetGetCache: { - delete: jest.fn(), + delete: vi.fn(), }, })); // Mock getExtensionsRegistry at module level - returns undefined by default -const mockGetExtensionsRegistry = jest.fn(() => ({ get: () => undefined })); -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), +const mockGetExtensionsRegistry = vi.fn(() => ({ get: () => undefined })); +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), getExtensionsRegistry: () => mockGetExtensionsRegistry(), })); -const mockedCachedSupersetGet = jest.mocked(cachedSupersetGet); -const mockedSupersetGetCacheDelete = jest.mocked(supersetGetCache.delete); -const mockExtension = jest.fn(); +const mockedCachedSupersetGet = vi.mocked(cachedSupersetGet); +const mockedSupersetGetCacheDelete = vi.mocked(supersetGetCache.delete); +const mockExtension = vi.fn(); // Helper to configure extension mock for extension path tests function setupExtensionMock() { mockGetExtensionsRegistry.mockReturnValue({ - get: jest.fn((key: any) => + get: vi.fn((key: any) => key === 'load.drillby.options' ? mockExtension : undefined, ) as any, }); } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterEach(() => { diff --git a/superset-frontend/src/hooks/apiResources/queries.test.ts b/superset-frontend/src/hooks/apiResources/queries.test.ts index 61f14c0ba31..66174d260a3 100644 --- a/superset-frontend/src/hooks/apiResources/queries.test.ts +++ b/superset-frontend/src/hooks/apiResources/queries.test.ts @@ -65,7 +65,7 @@ const fakeApiResult = { ], }; -afterEach(() => { +beforeEach(() => { fetchMock.clearHistory().removeRoutes(); act(() => { store.dispatch(api.util.resetApiState()); diff --git a/superset-frontend/src/hooks/apiResources/queryValidations.test.ts b/superset-frontend/src/hooks/apiResources/queryValidations.test.ts index 19be5595ed3..18b84ea8fc6 100644 --- a/superset-frontend/src/hooks/apiResources/queryValidations.test.ts +++ b/superset-frontend/src/hooks/apiResources/queryValidations.test.ts @@ -92,6 +92,7 @@ test('returns api response mapping json result', async () => { }); test('returns cached data without api request', async () => { + fetchMock.clearHistory(); const queryValidationApiRoute = `glob:*/api/v1/database/${expectDbId}/validate_sql/`; fetchMock.post(queryValidationApiRoute, fakeApiResult); const { result, waitFor, rerender } = renderHook( diff --git a/superset-frontend/src/hooks/apiResources/schemas.test.ts b/superset-frontend/src/hooks/apiResources/schemas.test.ts index 63516b7a49a..93d176ba1b6 100644 --- a/superset-frontend/src/hooks/apiResources/schemas.test.ts +++ b/superset-frontend/src/hooks/apiResources/schemas.test.ts @@ -64,7 +64,7 @@ describe('useSchemas hook', () => { const forceRefresh = false; const schemaApiRoute = `glob:*/api/v1/database/${expectDbId}/schemas/*`; fetchMock.get(schemaApiRoute, fakeApiResult); - const onSuccess = jest.fn(); + const onSuccess = vi.fn(); const { result, waitFor } = renderHook( () => useSchemas({ @@ -136,7 +136,7 @@ describe('useSchemas hook', () => { fetchMock.get(schemaApiRoute, ({ url }) => url.includes(expectDbId) ? fakeApiResult : fakeApiResult2, ); - const onSuccess = jest.fn(); + const onSuccess = vi.fn(); const { result, rerender, waitFor } = renderHook( ({ dbId }) => useSchemas({ @@ -197,7 +197,7 @@ describe('useSchemas hook', () => { ? fakeApiResult3 : fakeApiResult2, ); - const onSuccess = jest.fn(); + const onSuccess = vi.fn(); const { result, rerender, waitFor } = renderHook( ({ dbId, catalog }) => useSchemas({ diff --git a/superset-frontend/src/hooks/apiResources/sqlLab.test.ts b/superset-frontend/src/hooks/apiResources/sqlLab.test.ts index 627d37da1e3..6012346597c 100644 --- a/superset-frontend/src/hooks/apiResources/sqlLab.test.ts +++ b/superset-frontend/src/hooks/apiResources/sqlLab.test.ts @@ -89,6 +89,7 @@ test('returns api response mapping json result', async () => { }); test('returns cached data without api request', async () => { + fetchMock.clearHistory(); const { result, waitFor, rerender } = renderHook( () => useSqlLabInitialState(), { diff --git a/superset-frontend/src/hooks/useBeforeUnload/useBeforeUnload.test.ts b/superset-frontend/src/hooks/useBeforeUnload/useBeforeUnload.test.ts index c11abec297e..fc3d83477a6 100644 --- a/superset-frontend/src/hooks/useBeforeUnload/useBeforeUnload.test.ts +++ b/superset-frontend/src/hooks/useBeforeUnload/useBeforeUnload.test.ts @@ -18,26 +18,27 @@ */ import { renderHook } from '@testing-library/react-hooks'; import { useBeforeUnload } from './index'; +import { Mock } from 'vitest'; function createMockEvent() { return { - preventDefault: jest.fn(), + preventDefault: vi.fn(), returnValue: undefined as string | undefined, } as unknown as BeforeUnloadEvent; } -let addEventListenerSpy: jest.SpyInstance; -let removeEventListenerSpy: jest.SpyInstance; +let addEventListenerSpy: Mock; +let removeEventListenerSpy: Mock; let getMockHandler: () => (e: BeforeUnloadEvent) => void; let handlers: Array<(e: BeforeUnloadEvent) => void>; beforeEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); + vi.clearAllMocks(); + vi.restoreAllMocks(); handlers = []; - addEventListenerSpy = jest + addEventListenerSpy = vi .spyOn(window, 'addEventListener') .mockImplementation((type, handler) => { if (type === 'beforeunload') { @@ -45,7 +46,7 @@ beforeEach(() => { } }); - removeEventListenerSpy = jest.spyOn(window, 'removeEventListener'); + removeEventListenerSpy = vi.spyOn(window, 'removeEventListener'); getMockHandler = () => handlers[handlers.length - 1]; }); @@ -118,7 +119,7 @@ test('should update handler when shouldWarn changes', () => { initialHandler(event); expect(event.preventDefault).not.toHaveBeenCalled(); - (event.preventDefault as jest.Mock).mockClear(); + (event.preventDefault as Mock).mockClear(); event.returnValue = undefined; const initialAddCalls = addEventListenerSpy.mock.calls.length; diff --git a/superset-frontend/src/hooks/useDebounceValue.test.ts b/superset-frontend/src/hooks/useDebounceValue.test.ts index 1a130b214e1..a3b435de265 100644 --- a/superset-frontend/src/hooks/useDebounceValue.test.ts +++ b/superset-frontend/src/hooks/useDebounceValue.test.ts @@ -21,8 +21,8 @@ import { act, renderHook } from '@testing-library/react-hooks'; import { useDebounceValue } from './useDebounceValue'; afterEach(() => { - jest.clearAllTimers(); - jest.useRealTimers(); + vi.clearAllTimers(); + vi.useRealTimers(); }); test('should return the initial value', () => { @@ -31,7 +31,7 @@ test('should return the initial value', () => { }); test('should update debounced value after delay', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const { result, rerender } = renderHook( ({ value, delay }) => useDebounceValue(value, delay), { initialProps: { value: 'hello', delay: 1000 } }, @@ -40,20 +40,20 @@ test('should update debounced value after delay', async () => { expect(result.current).toBe('hello'); act(() => { rerender({ value: 'world', delay: 1000 }); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); }); expect(result.current).toBe('hello'); act(() => { - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); }); expect(result.current).toBe('world'); }); test('should cancel previous timeout when value changes', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); const { result, rerender } = renderHook( ({ value, delay }) => useDebounceValue(value, delay), { initialProps: { value: 'hello', delay: 1000 } }, @@ -62,21 +62,21 @@ test('should cancel previous timeout when value changes', async () => { expect(result.current).toBe('hello'); rerender({ value: 'world', delay: 1000 }); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); rerender({ value: 'foo', delay: 1000 }); - jest.advanceTimersByTime(500); + vi.advanceTimersByTime(500); expect(result.current).toBe('hello'); }); test('should cancel the timeout when unmounting', async () => { - jest.useFakeTimers(); - jest.spyOn(global, 'clearTimeout'); + vi.useFakeTimers(); + vi.spyOn(global, 'clearTimeout'); const { result, unmount } = renderHook(() => useDebounceValue('hello', 1000)); expect(result.current).toBe('hello'); unmount(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); expect(clearTimeout).toHaveBeenCalled(); }); diff --git a/superset-frontend/src/middleware/asyncEvent.test.ts b/superset-frontend/src/middleware/asyncEvent.test.ts index 10da96099dd..d037da88603 100644 --- a/superset-frontend/src/middleware/asyncEvent.test.ts +++ b/superset-frontend/src/middleware/asyncEvent.test.ts @@ -21,12 +21,12 @@ import WS from 'jest-websocket-mock'; import { parseErrorJson, isFeatureEnabled } from '@superset-ui/core'; import * as asyncEvent from 'src/middleware/asyncEvent'; -jest.mock('@superset-ui/core', () => ({ - ...jest.requireActual('@superset-ui/core'), - isFeatureEnabled: jest.fn(), +vi.mock('@superset-ui/core', async importActual => ({ + ...(await importActual()), + isFeatureEnabled: vi.fn(), })); -const mockedIsFeatureEnabled = isFeatureEnabled as jest.Mock; +const mockedIsFeatureEnabled = isFeatureEnabled as vi.Mock; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('asyncEvent middleware', () => { diff --git a/superset-frontend/src/middleware/logger.test.ts b/superset-frontend/src/middleware/logger.test.ts index 5b989e21713..7805f57f2a1 100644 --- a/superset-frontend/src/middleware/logger.test.ts +++ b/superset-frontend/src/middleware/logger.test.ts @@ -24,6 +24,7 @@ import { LOG_ACTIONS_SPA_NAVIGATION, } from 'src/logger/LogUtils'; import { Dispatch } from 'redux'; +import { Mock } from 'vitest'; interface LogEventAction { type: typeof LOG_EVENT; @@ -36,7 +37,7 @@ interface LogEventAction { // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('logger middleware', () => { const dashboardId = 123; - const next: jest.Mock = jest.fn(); + const next: Mock = vi.fn(); // Mock store with minimal state needed for tests const mockStore = { getState: () => ({ @@ -59,22 +60,22 @@ describe('logger middleware', () => { }; beforeAll(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterAll(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); - let postStub: jest.SpyInstance; + let postStub: Mock; beforeEach(() => { - postStub = jest + postStub = vi .spyOn(SupersetClient, 'post') .mockImplementation(() => undefined as any); }); afterEach(() => { next.mockClear(); postStub.mockRestore(); - jest.setSystemTime(0); + vi.setSystemTime(0); }); test('should listen to LOG_EVENT action type', () => { @@ -92,7 +93,7 @@ describe('logger middleware', () => { (logger as Function)(mockStore)(next)(action); expect(next.mock.calls.length).toBe(0); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(postStub.mock.calls.length).toBe(1); expect(postStub.mock.calls[0][0].endpoint).toMatch('/superset/log/'); }); @@ -114,9 +115,9 @@ describe('logger middleware', () => { eventData: { path: `/dashboard/${dashboardId}/` }, }, }); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); fetchLog(action); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(postStub.mock.calls.length).toBe(2); const { events } = postStub.mock.calls[1][0].postPayload; const mockEventdata = action.payload.eventData; @@ -146,14 +147,14 @@ describe('logger middleware', () => { (logger as Function)(mockStore)(next)(action); (logger as Function)(mockStore)(next)(action); (logger as Function)(mockStore)(next)(action); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(postStub.mock.calls.length).toBe(1); expect(postStub.mock.calls[0][0].postPayload.events).toHaveLength(3); }); test('should use navigator.sendBeacon if it exists', () => { - const beaconMock = jest.fn(); + const beaconMock = vi.fn(); Object.defineProperty(navigator, 'sendBeacon', { writable: true, value: beaconMock, @@ -161,7 +162,7 @@ describe('logger middleware', () => { (logger as Function)(mockStore)(next)(action); expect(beaconMock.mock.calls.length).toBe(0); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(beaconMock.mock.calls.length).toBe(1); const endpoint = beaconMock.mock.calls[0][0]; @@ -169,7 +170,7 @@ describe('logger middleware', () => { }); test('should pass a guest token to sendBeacon if present', () => { - const beaconMock = jest.fn(); + const beaconMock = vi.fn(); Object.defineProperty(navigator, 'sendBeacon', { writable: true, value: beaconMock, @@ -178,7 +179,7 @@ describe('logger middleware', () => { (logger as Function)(mockStore)(next)(action); expect(beaconMock.mock.calls.length).toBe(0); - jest.advanceTimersByTime(2000); + vi.advanceTimersByTime(2000); expect(beaconMock.mock.calls.length).toBe(1); const formData = beaconMock.mock.calls[0][1]; diff --git a/superset-frontend/src/theme/tests/ThemeController.test.ts b/superset-frontend/src/theme/tests/ThemeController.test.ts index 278d85f8c26..71725684273 100644 --- a/superset-frontend/src/theme/tests/ThemeController.test.ts +++ b/superset-frontend/src/theme/tests/ThemeController.test.ts @@ -31,22 +31,21 @@ import type { } from 'src/types/bootstrapTypes'; import getBootstrapData from 'src/utils/getBootstrapData'; import { LocalStorageAdapter, ThemeController } from '../ThemeController'; +import { Mock } from 'vitest'; -jest.mock('../../utils/getBootstrapData'); -const mockGetBootstrapData = getBootstrapData as jest.MockedFunction< - typeof getBootstrapData ->; +vi.mock('../../utils/getBootstrapData'); +const mockGetBootstrapData = getBootstrapData as Mock; const mockLocalStorage = { - getItem: jest.fn(), - setItem: jest.fn(), - removeItem: jest.fn(), - clear: jest.fn(), + getItem: vi.fn(), + setItem: vi.fn(), + removeItem: vi.fn(), + clear: vi.fn(), }; -const mockMatchMedia = jest.fn(); -const mockThemeFromConfig = jest.fn(); -const mockSetConfig = jest.fn(); +const mockMatchMedia = vi.fn(); +const mockThemeFromConfig = vi.fn(); +const mockSetConfig = vi.fn(); // Mock data constants const DEFAULT_THEME: AnyThemeConfig = { @@ -112,11 +111,11 @@ const createController = ( }); // Shared console spies -const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(); -const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); +const consoleSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); +const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); // Setup DOM environment Object.defineProperty(window, 'localStorage', { @@ -131,8 +130,8 @@ beforeEach(() => { mockMatchMedia.mockReturnValue({ matches: false, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }); mockSetConfig.mockImplementation(() => {}); @@ -288,7 +287,7 @@ test('ThemeController handles only default theme', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); expect(() => controller.setThemeMode(ThemeMode.DARK)).toThrow( 'Theme mode changes are not allowed when only one theme is available', @@ -324,7 +323,7 @@ test('ThemeController handles only dark theme', () => { expect(controller.canSetMode()).toBe(true); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); expect(mockSetConfig).toHaveBeenCalledTimes(1); }); @@ -410,7 +409,7 @@ test('ThemeController updates theme when allowed', () => { test('ThemeController changes theme mode when allowed', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); @@ -443,7 +442,7 @@ test('ThemeController handles missing theme gracefully', () => { test('ThemeController does not change mode if already set', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.SYSTEM); @@ -471,8 +470,8 @@ test('ThemeController resets to default theme', () => { test('ThemeController listens to system theme changes', () => { const mockMediaQuery = { matches: false, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }; mockMatchMedia.mockReturnValue(mockMediaQuery); @@ -488,8 +487,8 @@ test('ThemeController listens to system theme changes', () => { test('ThemeController updates theme when system preference changes and mode is SYSTEM', () => { const mockMediaQuery = { matches: false, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }; mockMatchMedia.mockReturnValue(mockMediaQuery); @@ -507,8 +506,8 @@ test('ThemeController updates theme when system preference changes and mode is S test('ThemeController does not update theme when mode is not SYSTEM', () => { const mockMediaQuery = { matches: false, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }; mockMatchMedia.mockReturnValue(mockMediaQuery); @@ -535,8 +534,8 @@ test('ThemeController switches to dark theme when system is dark and mode is SYS const mockMediaQueryDark = { matches: true, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }; mockMatchMedia.mockReturnValue(mockMediaQueryDark); @@ -556,7 +555,7 @@ test('ThemeController switches to dark theme when system is dark and mode is SYS test('ThemeController saves theme mode to localStorage', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); @@ -603,7 +602,7 @@ test('ThemeController handles theme with token structure', () => { }, }; - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setTheme(customTheme); @@ -622,7 +621,7 @@ test('ThemeController handles theme with token structure', () => { test('ThemeController preserves algorithm property from dark theme', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); @@ -641,7 +640,7 @@ test('ThemeController preserves algorithm property from dark theme', () => { test('ThemeController handles theme without algorithm property', () => { const controller = createController(); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DEFAULT); @@ -702,7 +701,7 @@ test('ThemeController handles valid algorithm combinations', () => { ] as ThemeAlgorithm[], }; - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setTheme(themeWithAlgorithm); @@ -739,7 +738,7 @@ test('ThemeController handles invalid algorithm combinations', () => { algorithm: ['invalid', 'combination'] as any as ThemeAlgorithm[], }; - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setTheme(themeWithInvalidAlgorithm); @@ -753,7 +752,7 @@ test('ThemeController handles invalid algorithm combinations', () => { // Change Callbacks tests test('ThemeController calls callback on theme change', () => { - const callback = jest.fn(); + const callback = vi.fn(); const controller = createController({ onChange: callback }); controller.setThemeMode(ThemeMode.DARK); @@ -763,10 +762,10 @@ test('ThemeController calls callback on theme change', () => { }); test('ThemeController registers additional callbacks', () => { - const callback = jest.fn(); + const callback = vi.fn(); const controller = createController({ onChange: callback }); - const additionalCallback = jest.fn(); + const additionalCallback = vi.fn(); controller.onChange(additionalCallback); controller.setThemeMode(ThemeMode.DARK); @@ -776,10 +775,10 @@ test('ThemeController registers additional callbacks', () => { }); test('ThemeController unsubscribes callbacks', () => { - const callback = jest.fn(); + const callback = vi.fn(); const controller = createController({ onChange: callback }); - const additionalCallback = jest.fn(); + const additionalCallback = vi.fn(); const unsubscribe = controller.onChange(additionalCallback); unsubscribe(); @@ -789,10 +788,10 @@ test('ThemeController unsubscribes callbacks', () => { }); test('ThemeController handles callback errors', () => { - const callback = jest.fn(); + const callback = vi.fn(); const controller = createController({ onChange: callback }); - const errorCallback = jest.fn().mockImplementation(() => { + const errorCallback = vi.fn().mockImplementation(() => { throw new Error('Callback error'); }); @@ -815,7 +814,7 @@ test('ThemeController handles theme application errors', () => { throw new Error('Theme application error'); }); - const fallbackSpy = jest.spyOn(controller as any, 'fallbackToDefaultMode'); + const fallbackSpy = vi.spyOn(controller as any, 'fallbackToDefaultMode'); fallbackSpy.mockImplementation(() => { (controller as any).customizations = DEFAULT_THEME; (controller as any).currentMode = ThemeMode.DEFAULT; @@ -892,7 +891,7 @@ test('recovery flow: fetchSystemDefaultTheme returns theme → applies fetched t try { // Mock fetch to return a system default theme from API const systemTheme = { token: { colorPrimary: '#recovery-theme' } }; - const mockFetch = jest.fn().mockResolvedValueOnce({ + const mockFetch = vi.fn().mockResolvedValueOnce({ ok: true, json: async () => ({ result: [{ json_data: JSON.stringify(systemTheme) }], @@ -938,7 +937,7 @@ test('recovery flow: both API fetches fail → falls back to cached default them try { // Mock fetch to fail for both API endpoints - const mockFetch = jest.fn().mockRejectedValue(new Error('Network error')); + const mockFetch = vi.fn().mockRejectedValue(new Error('Network error')); global.fetch = mockFetch; // Track setConfig calls @@ -980,7 +979,7 @@ test('recovery flow: fetched theme fails to apply → falls back to cached defau try { // Mock fetch to return a theme const systemTheme = { token: { colorPrimary: '#bad-theme' } }; - const mockFetch = jest.fn().mockResolvedValueOnce({ + const mockFetch = vi.fn().mockResolvedValueOnce({ ok: true, json: async () => ({ result: [{ json_data: JSON.stringify(systemTheme) }], @@ -1033,8 +1032,8 @@ test('recovery flow: fetched theme fails to apply → falls back to cached defau test('ThemeController cleans up listeners on destroy', () => { const mockMediaQueryInstance = { matches: false, - addEventListener: jest.fn(), - removeEventListener: jest.fn(), + addEventListener: vi.fn(), + removeEventListener: vi.fn(), }; mockMatchMedia.mockReturnValue(mockMediaQueryInstance); @@ -1060,7 +1059,7 @@ test('setThemeConfig sets complete theme configuration', () => { const controller = createController({ defaultTheme: { token: {} } }); - jest.clearAllMocks(); + vi.clearAllMocks(); const themeConfig = { theme_default: DEFAULT_THEME, @@ -1092,7 +1091,7 @@ test('setThemeConfig handles theme_default only', () => { const controller = createController({ defaultTheme: { token: {} } }); - jest.clearAllMocks(); + vi.clearAllMocks(); const themeConfig = { theme_default: DEFAULT_THEME, @@ -1122,7 +1121,7 @@ test('setThemeConfig handles theme_default and theme_dark without settings', () const controller = createController({ defaultTheme: { token: {} } }); - jest.clearAllMocks(); + vi.clearAllMocks(); const themeConfig = { theme_default: DEFAULT_THEME, @@ -1138,7 +1137,7 @@ test('setThemeConfig handles theme_default and theme_dark without settings', () }), ); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); expect(mockSetConfig).toHaveBeenCalledTimes(1); @@ -1160,7 +1159,7 @@ test('setThemeConfig applies appropriate theme after configuration', () => { const controller = createController({ defaultTheme: { token: {} } }); - jest.clearAllMocks(); + vi.clearAllMocks(); const themeConfig = { theme_default: { @@ -1222,7 +1221,7 @@ test('setThemeConfig preserves existing theme mode when possible', () => { controller.setThemeMode(ThemeMode.DARK); const initialMode = controller.getCurrentMode(); - jest.clearAllMocks(); + vi.clearAllMocks(); const themeConfig = { theme_default: DEFAULT_THEME, @@ -1244,7 +1243,7 @@ test('setThemeConfig triggers onChange callbacks', () => { const controller = createController({ defaultTheme: { token: {} } }); - const changeCallback = jest.fn(); + const changeCallback = vi.fn(); controller.onChange(changeCallback); const themeConfig = { @@ -1330,7 +1329,7 @@ test('setThemeMode clears dev override and crud theme from storage', () => { }); Reflect.set(controller, 'crudThemeId', '123'); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DARK); @@ -1357,7 +1356,7 @@ test('setThemeMode can be called with same mode when overrides exist', () => { themeObject: mockThemeObject, }); - jest.clearAllMocks(); + vi.clearAllMocks(); Reflect.set(controller, 'devThemeOverride', { token: { colorPrimary: '#ff0000' }, @@ -1390,7 +1389,7 @@ test('setThemeMode with no override and same mode does not trigger update', () = controller.setThemeMode(ThemeMode.DEFAULT); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setThemeMode(ThemeMode.DEFAULT); @@ -1453,7 +1452,7 @@ test('clearLocalOverrides removes dev override, crud theme, and applied theme ID themeObject: mockThemeObject, }); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.clearLocalOverrides(); @@ -1503,7 +1502,7 @@ test('setAppliedThemeId stores theme ID in storage', () => { themeObject: mockThemeObject, }); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setAppliedThemeId(123); @@ -1519,7 +1518,7 @@ test('setAppliedThemeId removes theme ID when null is passed', () => { themeObject: mockThemeObject, }); - jest.clearAllMocks(); + vi.clearAllMocks(); controller.setAppliedThemeId(null); diff --git a/superset-frontend/src/utils/DebouncedMessageQueue.test.ts b/superset-frontend/src/utils/DebouncedMessageQueue.test.ts index fabfb5068cf..00d54a9df4c 100644 --- a/superset-frontend/src/utils/DebouncedMessageQueue.test.ts +++ b/superset-frontend/src/utils/DebouncedMessageQueue.test.ts @@ -28,7 +28,7 @@ describe('DebouncedMessageQueue', () => { }); test('should accept custom configuration options', () => { - const mockCallback = jest.fn(); + const mockCallback = vi.fn(); const queue = new DebouncedMessageQueue({ callback: mockCallback, sizeThreshold: 500, @@ -38,7 +38,7 @@ describe('DebouncedMessageQueue', () => { }); test('should append items to the queue', () => { - const mockCallback = jest.fn(); + const mockCallback = vi.fn(); const queue = new DebouncedMessageQueue({ callback: mockCallback }); const testEvent = { id: 1, message: 'test' }; @@ -54,7 +54,7 @@ describe('DebouncedMessageQueue', () => { data: string; } - const mockCallback = jest.fn(); + const mockCallback = vi.fn(); const queue = new DebouncedMessageQueue({ callback: mockCallback, }); diff --git a/superset-frontend/src/utils/assetUrl.test.ts b/superset-frontend/src/utils/assetUrl.test.ts index f0de28b2c77..5bfcf27a633 100644 --- a/superset-frontend/src/utils/assetUrl.test.ts +++ b/superset-frontend/src/utils/assetUrl.test.ts @@ -19,10 +19,7 @@ import * as getBootstrapData from 'src/utils/getBootstrapData'; import { assetUrl, ensureStaticPrefix } from './assetUrl'; -const staticAssetsPrefixMock = jest.spyOn( - getBootstrapData, - 'staticAssetsPrefix', -); +const staticAssetsPrefixMock = vi.spyOn(getBootstrapData, 'staticAssetsPrefix'); const resourcePath = '/endpoint/img.png'; const absoluteResourcePath = `https://cdn.domain.com/static${resourcePath}`; diff --git a/superset-frontend/src/utils/cacheWrapper.test.ts b/superset-frontend/src/utils/cacheWrapper.test.ts index 67ddf57e477..480a74914c8 100644 --- a/superset-frontend/src/utils/cacheWrapper.test.ts +++ b/superset-frontend/src/utils/cacheWrapper.test.ts @@ -21,7 +21,7 @@ import { cacheWrapper } from 'src/utils/cacheWrapper'; // eslint-disable-next-line no-restricted-globals -- TODO: Migrate from describe blocks describe('cacheWrapper', () => { const fnResult = 'fnResult'; - const fn = jest.fn().mockReturnValue(fnResult); + const fn = vi.fn().mockReturnValue(fnResult); let wrappedFn: (a: number, b: number) => string; @@ -31,7 +31,7 @@ describe('cacheWrapper', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('calls fn with its arguments once when the key is not found', () => { diff --git a/superset-frontend/src/utils/export.test.ts b/superset-frontend/src/utils/export.test.ts index 1a0f96be716..72e3f185a95 100644 --- a/superset-frontend/src/utils/export.test.ts +++ b/superset-frontend/src/utils/export.test.ts @@ -20,37 +20,42 @@ import { SupersetClient } from '@superset-ui/core'; import { logging } from '@apache-superset/core'; import contentDisposition from 'content-disposition'; import handleResourceExport from './export'; +import { Mock } from 'vitest'; // Mock dependencies -jest.mock('@superset-ui/core', () => ({ +vi.mock('@superset-ui/core', () => ({ SupersetClient: { - get: jest.fn(), + get: vi.fn(), }, })); -jest.mock('@apache-superset/core', () => ({ +vi.mock('@apache-superset/core', () => ({ logging: { - warn: jest.fn(), - error: jest.fn(), + warn: vi.fn(), + error: vi.fn(), }, })); -jest.mock('content-disposition'); +vi.mock('content-disposition'); + +const { ensureAppRootMock } = vi.hoisted(() => ({ + ensureAppRootMock: vi.fn((path: string) => path), +})); // Default no-op mock for pathUtils; specific tests customize ensureAppRoot to simulate app root prefixing -jest.mock('./pathUtils', () => ({ - ensureAppRoot: jest.fn((path: string) => path), +vi.mock('./pathUtils', () => ({ + ensureAppRoot: ensureAppRootMock, })); let mockBlob: Blob; let mockResponse: Response; -let createElementSpy: jest.SpyInstance; -let createObjectURLSpy: jest.SpyInstance; -let revokeObjectURLSpy: jest.SpyInstance; +let createElementSpy: Mock; +let createObjectURLSpy: Mock; +let revokeObjectURLSpy: Mock; beforeEach(() => { // Reset all mocks - jest.clearAllMocks(); + vi.clearAllMocks(); // Mock Blob mockBlob = new Blob(['test data'], { type: 'application/zip' }); @@ -60,31 +65,31 @@ beforeEach(() => { headers: new Headers({ 'Content-Disposition': 'attachment; filename="dashboard_export.zip"', }), - blob: jest.fn().mockResolvedValue(mockBlob), + blob: vi.fn().mockResolvedValue(mockBlob), } as unknown as Response; // Mock SupersetClient.get - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); // Mock DOM APIs const mockAnchor = document.createElement('a'); - mockAnchor.click = jest.fn(); - createElementSpy = jest + mockAnchor.click = vi.fn(); + createElementSpy = vi .spyOn(document, 'createElement') .mockReturnValue(mockAnchor); - jest.spyOn(document.body, 'appendChild').mockImplementation(() => mockAnchor); - jest.spyOn(document.body, 'removeChild').mockImplementation(() => mockAnchor); + vi.spyOn(document.body, 'appendChild').mockImplementation(() => mockAnchor); + vi.spyOn(document.body, 'removeChild').mockImplementation(() => mockAnchor); // Mock URL.createObjectURL and revokeObjectURL - createObjectURLSpy = jest + createObjectURLSpy = vi .spyOn(window.URL, 'createObjectURL') .mockReturnValue('blob:mock-url'); // Create revokeObjectURL if it doesn't exist if (!window.URL.revokeObjectURL) { - window.URL.revokeObjectURL = jest.fn(); + window.URL.revokeObjectURL = vi.fn(); } - revokeObjectURLSpy = jest.spyOn(window.URL, 'revokeObjectURL'); + revokeObjectURLSpy = vi.spyOn(window.URL, 'revokeObjectURL'); }); afterEach(() => { @@ -96,7 +101,7 @@ afterEach(() => { }); test('exports resource with correct endpoint and headers', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1, 2, 3], doneMock); expect(SupersetClient.get).toHaveBeenCalledWith({ @@ -109,7 +114,7 @@ test('exports resource with correct endpoint and headers', async () => { }); test('creates blob and triggers download', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); // Check that blob was created @@ -135,7 +140,7 @@ test('creates blob and triggers download', async () => { }); test('calls done callback on success', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); expect(doneMock).toHaveBeenCalled(); @@ -144,11 +149,11 @@ test('calls done callback on success', async () => { test('uses default filename when Content-Disposition is missing', async () => { mockResponse = { headers: new Headers(), - blob: jest.fn().mockResolvedValue(mockBlob), + blob: vi.fn().mockResolvedValue(mockBlob), } as unknown as Response; - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('chart', [42], doneMock); const anchor = document.createElement('a'); @@ -156,11 +161,11 @@ test('uses default filename when Content-Disposition is missing', async () => { }); test('handles Content-Disposition parsing errors gracefully', async () => { - (contentDisposition.parse as jest.Mock).mockImplementationOnce(() => { + (contentDisposition.parse as Mock).mockImplementationOnce(() => { throw new Error('Invalid header'); }); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); // Should fall back to default filename @@ -171,9 +176,9 @@ test('handles Content-Disposition parsing errors gracefully', async () => { test('handles API errors and calls done callback', async () => { const apiError = new Error('API Error'); - (SupersetClient.get as jest.Mock).mockRejectedValue(apiError); + (SupersetClient.get as Mock).mockRejectedValue(apiError); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await expect( handleResourceExport('dashboard', [1], doneMock), @@ -184,10 +189,10 @@ test('handles API errors and calls done callback', async () => { test('handles blob conversion errors', async () => { const blobError = new Error('Blob conversion failed'); - mockResponse.blob = jest.fn().mockRejectedValue(blobError); - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + mockResponse.blob = vi.fn().mockRejectedValue(blobError); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await expect( handleResourceExport('dashboard', [1], doneMock), @@ -197,7 +202,7 @@ test('handles blob conversion errors', async () => { }); test('exports multiple resources with correct IDs', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dataset', [10, 20, 30, 40], doneMock); expect(SupersetClient.get).toHaveBeenCalledWith( @@ -208,12 +213,12 @@ test('exports multiple resources with correct IDs', async () => { }); test('parses filename from Content-Disposition with quotes', async () => { - (contentDisposition.parse as jest.Mock).mockReturnValueOnce({ + (contentDisposition.parse as Mock).mockReturnValueOnce({ type: 'attachment', parameters: { filename: 'my_custom_export.zip' }, }); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); const anchor = document.createElement('a'); @@ -228,11 +233,11 @@ test('warns when export exceeds maximum blob size', async () => { 'Content-Length': largeFileSize.toString(), 'Content-Disposition': 'attachment; filename="large_export.zip"', }), - blob: jest.fn().mockResolvedValue(mockBlob), + blob: vi.fn().mockResolvedValue(mockBlob), } as unknown as Response; - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); expect(logging.warn).toHaveBeenCalledWith( @@ -242,7 +247,7 @@ test('warns when export exceeds maximum blob size', async () => { }); test('handles various resource types', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); expect(SupersetClient.get).toHaveBeenCalledWith( @@ -284,9 +289,9 @@ test('handles various resource types', async () => { test('handles network errors and logs them', async () => { const networkError = new Error('Network request failed'); - (SupersetClient.get as jest.Mock).mockRejectedValue(networkError); + (SupersetClient.get as Mock).mockRejectedValue(networkError); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await expect( handleResourceExport('dashboard', [1], doneMock), @@ -301,9 +306,9 @@ test('handles network errors and logs them', async () => { test('handles 404 errors when resource not found', async () => { const notFoundError = new Error('Not found'); - (SupersetClient.get as jest.Mock).mockRejectedValue(notFoundError); + (SupersetClient.get as Mock).mockRejectedValue(notFoundError); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await expect( handleResourceExport('dashboard', [999], doneMock), @@ -318,11 +323,11 @@ test('handles empty response from server', async () => { headers: new Headers({ 'Content-Disposition': 'attachment; filename="empty.zip"', }), - blob: jest.fn().mockResolvedValue(emptyBlob), + blob: vi.fn().mockResolvedValue(emptyBlob), } as unknown as Response; - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [1], doneMock); expect(window.URL.createObjectURL).toHaveBeenCalledWith(emptyBlob); @@ -331,16 +336,16 @@ test('handles empty response from server', async () => { test('cleans up blob URL even when download fails', async () => { const mockAnchor = document.createElement('a'); - mockAnchor.click = jest.fn().mockImplementation(() => { + mockAnchor.click = vi.fn().mockImplementation(() => { throw new Error('Click failed'); }); createElementSpy.mockRestore(); - createElementSpy = jest + createElementSpy = vi .spyOn(document, 'createElement') .mockReturnValue(mockAnchor); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await expect( handleResourceExport('dashboard', [1], doneMock), @@ -356,15 +361,15 @@ test('handles malformed Content-Disposition header', async () => { headers: new Headers({ 'Content-Disposition': 'not-a-valid-header', }), - blob: jest.fn().mockResolvedValue(mockBlob), + blob: vi.fn().mockResolvedValue(mockBlob), } as unknown as Response; - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - (contentDisposition.parse as jest.Mock).mockImplementationOnce(() => { + (contentDisposition.parse as Mock).mockImplementationOnce(() => { throw new Error('Parse error'); }); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dataset', [5], doneMock); // Should fall back to default filename @@ -379,11 +384,11 @@ test('handles malformed Content-Disposition header', async () => { test('handles missing headers object', async () => { mockResponse = { headers: new Headers(), - blob: jest.fn().mockResolvedValue(mockBlob), + blob: vi.fn().mockResolvedValue(mockBlob), } as unknown as Response; - (SupersetClient.get as jest.Mock).mockResolvedValue(mockResponse); + (SupersetClient.get as Mock).mockResolvedValue(mockResponse); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('chart', [7], doneMock); const anchor = document.createElement('a'); @@ -392,7 +397,7 @@ test('handles missing headers object', async () => { }); test('handles export with empty IDs array', async () => { - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport('dashboard', [], doneMock); expect(SupersetClient.get).toHaveBeenCalledWith( @@ -402,8 +407,6 @@ test('handles export with empty IDs array', async () => { ); }); -const { ensureAppRoot } = jest.requireMock('./pathUtils'); - const doublePrefixTestCases = [ { name: 'subdirectory prefix', @@ -429,11 +432,9 @@ test.each(doublePrefixTestCases)( 'handleResourceExport endpoint should not include app prefix: $name', async ({ appRoot, resource, ids }) => { // Simulate real ensureAppRoot behavior: prepend the appRoot - (ensureAppRoot as jest.Mock).mockImplementation( - (path: string) => `${appRoot}${path}`, - ); + ensureAppRootMock.mockImplementation((path: string) => `${appRoot}${path}`); - const doneMock = jest.fn(); + const doneMock = vi.fn(); await handleResourceExport(resource, ids, doneMock); // The endpoint passed to SupersetClient.get should NOT have the appRoot prefix @@ -441,13 +442,11 @@ test.each(doublePrefixTestCases)( const expectedEndpoint = `/api/v1/${resource}/export/?q=!(${ids.join(',')})`; // Explicitly verify no prefix in endpoint - this will fail if ensureAppRoot is used - const callArgs = (SupersetClient.get as jest.Mock).mock.calls.slice( - -1, - )[0][0]; + const callArgs = (SupersetClient.get as Mock).mock.calls.slice(-1)[0][0]; expect(callArgs.endpoint).not.toContain(appRoot); expect(callArgs.endpoint).toBe(expectedEndpoint); // Reset mock for next test - (ensureAppRoot as jest.Mock).mockImplementation((path: string) => path); + ensureAppRootMock.mockImplementation((path: string) => path); }, ); diff --git a/superset-frontend/src/utils/getBootstrapData.test.ts b/superset-frontend/src/utils/getBootstrapData.test.ts index 14b9c686a55..cad83533a1a 100644 --- a/superset-frontend/src/utils/getBootstrapData.test.ts +++ b/superset-frontend/src/utils/getBootstrapData.test.ts @@ -33,7 +33,7 @@ describe('getBootstrapData and helpers', () => { document.body.innerHTML = ''; // Reset module to clear cachedBootstrapData - jest.resetModules(); + vi.resetModules(); const { default: getBootstrapData } = await import('./getBootstrapData'); const bootstrapData = getBootstrapData(); expect(bootstrapData).toEqual(DEFAULT_BOOTSTRAP_DATA); @@ -50,7 +50,7 @@ describe('getBootstrapData and helpers', () => { document.body.innerHTML = `
`; // Reset modules and re-import the module so that cachedBootstrapData is clear. - jest.resetModules(); + vi.resetModules(); const { default: getBootstrapData } = await import('./getBootstrapData'); const bootstrapData = getBootstrapData(); expect(bootstrapData).toEqual(customData); @@ -70,7 +70,7 @@ describe('getBootstrapData and helpers', () => { document.body.innerHTML = `
`; // Reset modules and re-import the module so that cachedBootstrapData is clear. - jest.resetModules(); + vi.resetModules(); const { default: getBootstrapData, applicationRoot, @@ -90,7 +90,7 @@ describe('getBootstrapData and helpers', () => { document.body.innerHTML = ''; // Reset module to clear cachedBootstrapData and re-run computed values. - jest.resetModules(); + vi.resetModules(); const { default: getBootstrapData, applicationRoot, @@ -120,7 +120,7 @@ describe('getBootstrapData and helpers', () => { document.body.innerHTML = `
`; // Reset modules and re-import the module so that cachedBootstrapData is clear. - jest.resetModules(); + vi.resetModules(); const { default: getBootstrapData, applicationRoot, diff --git a/superset-frontend/src/utils/getChartFormDiffs/getChartFormDiffs.test.ts b/superset-frontend/src/utils/getChartFormDiffs/getChartFormDiffs.test.ts index 45e36233bf4..1620c64d9e7 100644 --- a/superset-frontend/src/utils/getChartFormDiffs/getChartFormDiffs.test.ts +++ b/superset-frontend/src/utils/getChartFormDiffs/getChartFormDiffs.test.ts @@ -19,7 +19,7 @@ import { JsonObject } from '@superset-ui/core'; import { alterForComparison, getChartFormDiffs, isEqualish } from '.'; -jest.mock('../sanitizeFormData', () => ({ +vi.mock('../sanitizeFormData', () => ({ sanitizeFormData: (fd: JsonObject): JsonObject => ({ ...fd, _sanitized: true, diff --git a/superset-frontend/src/utils/parseCookie.test.ts b/superset-frontend/src/utils/parseCookie.test.ts index a63f6e59446..10c1b59d629 100644 --- a/superset-frontend/src/utils/parseCookie.test.ts +++ b/superset-frontend/src/utils/parseCookie.test.ts @@ -22,7 +22,7 @@ import parseCookie from 'src/utils/parseCookie'; describe('parseCookie', () => { let cookieVal = ''; Object.defineProperty(document, 'cookie', { - get: jest.fn().mockImplementation(() => cookieVal), + get: vi.fn().mockImplementation(() => cookieVal), }); test('parses cookie strings', () => { cookieVal = 'val1=foo; val2=bar'; diff --git a/superset-frontend/src/utils/pathUtils.test.ts b/superset-frontend/src/utils/pathUtils.test.ts index bf24053e671..2355cdc0253 100644 --- a/superset-frontend/src/utils/pathUtils.test.ts +++ b/superset-frontend/src/utils/pathUtils.test.ts @@ -20,12 +20,12 @@ afterEach(() => { // Clean up the DOM document.body.innerHTML = ''; - jest.resetModules(); + vi.resetModules(); }); test('ensureAppRoot should add application root prefix to path with default root', async () => { document.body.innerHTML = ''; - jest.resetModules(); + vi.resetModules(); const { ensureAppRoot } = await import('./pathUtils'); expect(ensureAppRoot('/sqllab')).toBe('/sqllab'); @@ -39,7 +39,7 @@ test('ensureAppRoot should add application root prefix to path with custom subdi }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); // Import getBootstrapData first to initialize the cache await import('./getBootstrapData'); @@ -56,7 +56,7 @@ test('ensureAppRoot should handle paths without leading slash', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { ensureAppRoot } = await import('./pathUtils'); @@ -72,7 +72,7 @@ test('ensureAppRoot should handle paths with query strings', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { ensureAppRoot } = await import('./pathUtils'); @@ -85,7 +85,7 @@ test('ensureAppRoot should handle paths with query strings', async () => { test('makeUrl should create URL with default application root', async () => { document.body.innerHTML = ''; - jest.resetModules(); + vi.resetModules(); const { makeUrl } = await import('./pathUtils'); expect(makeUrl('/sqllab')).toBe('/sqllab'); @@ -99,7 +99,7 @@ test('makeUrl should create URL with subdirectory prefix', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { makeUrl } = await import('./pathUtils'); @@ -118,7 +118,7 @@ test('makeUrl should handle paths without leading slash', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { makeUrl } = await import('./pathUtils'); @@ -133,7 +133,7 @@ test('makeUrl should work with different subdirectory paths', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { makeUrl } = await import('./pathUtils'); @@ -149,7 +149,7 @@ test('makeUrl should handle URLs with anchors', async () => { }, }; document.body.innerHTML = `
`; - jest.resetModules(); + vi.resetModules(); await import('./getBootstrapData'); const { makeUrl } = await import('./pathUtils'); diff --git a/superset-frontend/src/visualizations/TimeTable/utils/sortUtils/sortUtils.test.ts b/superset-frontend/src/visualizations/TimeTable/utils/sortUtils/sortUtils.test.ts index 1be6a14fdcd..cd0e9c59724 100644 --- a/superset-frontend/src/visualizations/TimeTable/utils/sortUtils/sortUtils.test.ts +++ b/superset-frontend/src/visualizations/TimeTable/utils/sortUtils/sortUtils.test.ts @@ -21,7 +21,7 @@ import { vi } from 'vitest'; import { sortNumberWithMixedTypes } from './sortUtils'; vi.mock('src/utils/sortNumericValues', () => ({ - sortNumericValues: jest.fn((a, b, options) => { + sortNumericValues: vi.fn((a, b, options) => { const numA = Number(a); const numB = Number(b); diff --git a/superset-frontend/tsconfig.json b/superset-frontend/tsconfig.json index 9097f8a5082..43d95218108 100644 --- a/superset-frontend/tsconfig.json +++ b/superset-frontend/tsconfig.json @@ -9,8 +9,8 @@ "module": "esnext", "moduleResolution": "bundler", - "types": ["jest", "node"], - "typeRoots": ["src/types", "node_modules/@types"], + "types": ["vitest/globals", "node"], + "typeRoots": ["src/types", "node_modules/@types", "./node_modules"], /* Emit */ "declaration": true, diff --git a/superset-frontend/vitest.config.ts b/superset-frontend/vitest.config.ts index 6f58cbdffc3..fb50a31c6f3 100644 --- a/superset-frontend/vitest.config.ts +++ b/superset-frontend/vitest.config.ts @@ -19,8 +19,10 @@ import path from 'path'; import { defineConfig } from 'vitest/config'; +import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ + plugins: [tsconfigPaths()], test: { globals: true, env: { @@ -29,6 +31,12 @@ export default defineConfig({ WEBPACK_MODE: 'test', }, environment: 'jsdom', + environmentOptions: { + jsdom: { + url: 'http://localhost', + }, + }, + setupFiles: [path.resolve(__dirname, './spec/helpers/setup.ts')], include: ['./(spec|src|plugins|packages|tools)/**/*.test.ts'], exclude: [ './packages/generator-superset', @@ -39,7 +47,7 @@ export default defineConfig({ ], reporters: ['default'], coverage: { - enabled: true, + enabled: false, clean: true, reportsDirectory: './coverage', reporter: ['lcov', 'json-summary', 'html', 'text'], @@ -56,26 +64,49 @@ export default defineConfig({ 'dist/', ], }, - }, - resolve: { - alias: { - '\.(css|less|geojson)$': path.resolve( - __dirname, - './spec/__mocks__/mockExportObject.js', - ), - '\.(gif|ttf|eot|png|jpg)$': path.resolve( - __dirname, - './spec/__mocks__/mockExportString.js', - ), - '\.svg$': path.resolve(__dirname, './spec/__mocks__/svgrMock.tsx'), - src: path.resolve(__dirname, './src'), + alias: [ + // { + // find: new RegExp('\.(less|geojson)$'), + // replacement: path.resolve( + // __dirname, + // './spec/__mocks__/mockExportObject.js', + // ), + // }, + // { + // find: new RegExp('\.(gif|ttf|eot|png|jpg)$'), + // replacement: path.resolve( + // __dirname, + // './spec/__mocks__/mockExportString.js', + // ), + // }, + { + find: new RegExp('^src/(.*)$'), + replacement: path.resolve(__dirname, './src/$1'), + }, // Mapping plugins of superset-ui to source code - '@superset-ui': path.resolve(__dirname, './node_modules/@superset-ui'), + { + find: new RegExp('^@superset-ui/([^/]+)/(.*)$'), + replacement: path.resolve( + __dirname, + './node_modules/@superset-ui/$1/src/$2', + ), + }, + { + find: new RegExp('^@superset-ui/([^/]+)$'), + replacement: path.resolve( + __dirname, + './node_modules/@superset-ui/$1/src', + ), + }, // Mapping @apache-superset/core to local package - '@apache-superset/core': path.resolve( - __dirname, - './node_modules/superset-core', - ), - }, + { + find: new RegExp('^@apache-superset/core$'), + replacement: path.resolve(__dirname, './packages/superset-core/src'), + }, + { + find: new RegExp('^@apache-superset/core/(.*)$'), + replacement: path.resolve(__dirname, './packages/superset-core/src/$1'), + }, + ], }, });