From 47e79c3aec3d19e4f6543520b8f306130b9ea1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 14:03:08 +0100 Subject: [PATCH 01/23] Removed leftover webpack config --- test.frontend.webpack.config.js | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 test.frontend.webpack.config.js diff --git a/test.frontend.webpack.config.js b/test.frontend.webpack.config.js deleted file mode 100644 index d71ae6a6..00000000 --- a/test.frontend.webpack.config.js +++ /dev/null @@ -1,23 +0,0 @@ -import { merge } from "webpack-merge"; - -import prod from "./frontend.webpack.config.js"; - -export default (env, options) => - merge( - { - module: { - rules: [ - { - test: /src\/frontend\/.*\.(ts|svelte)$/u, - use: { - loader: "webpack-plugin-istanbul/loader", - options: { - extension: [".svelte", ".js", ".ts"], - }, - }, - }, - ], - }, - }, - prod(env, options), - ); From 9111ad4f47e62641010092fcc2945a8f752a2d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 14:16:40 +0100 Subject: [PATCH 02/23] Added basic vitest test --- backend.vite.config.ts | 5 + eslint.config.js | 3 + package-lock.json | 391 ++++++++++++++++- package.json | 6 +- {__tests__ => tests}/backend/doGet.test.ts | 17 +- tests/test-utils/gas-stubs.ts | 462 +++++++++++++++++++++ 6 files changed, 870 insertions(+), 14 deletions(-) rename {__tests__ => tests}/backend/doGet.test.ts (59%) create mode 100644 tests/test-utils/gas-stubs.ts diff --git a/backend.vite.config.ts b/backend.vite.config.ts index cfe92812..18649321 100644 --- a/backend.vite.config.ts +++ b/backend.vite.config.ts @@ -12,4 +12,9 @@ export default defineConfig({ outDir: "../dist", }, root: "src", + test: { + dir: "tests/backend", + mockReset: true, + root: ".", + }, }); diff --git a/eslint.config.js b/eslint.config.js index 26fe3db4..cbfb85ce 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -216,6 +216,9 @@ export default tseslint.config( }, }, }, + { + files: ["tests/**/*.test.ts", "tests/test-utils/gas-stubs.ts"], + }, { ...jest.configs["flat/recommended"], ...jest.configs["flat/style"], diff --git a/package-lock.json b/package-lock.json index 0b932259..35db6e01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,8 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.19.1", "vite": "^5.4.11", - "vite-plugin-singlefile": "^2.1.0" + "vite-plugin-singlefile": "^2.1.0", + "vitest": "^2.1.8" } }, "node_modules/@ampproject/remapping": { @@ -4529,6 +4530,119 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitest/expect": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", + "integrity": "sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.8.tgz", + "integrity": "sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.8.tgz", + "integrity": "sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.8.tgz", + "integrity": "sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.8", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.8.tgz", + "integrity": "sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.8", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.8.tgz", + "integrity": "sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.8.tgz", + "integrity": "sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.8", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -4783,6 +4897,16 @@ "node": ">=8" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -5139,6 +5263,16 @@ "dev": true, "license": "MIT" }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -5258,6 +5392,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -5292,6 +5443,16 @@ "dev": true, "license": "MIT" }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -5765,6 +5926,16 @@ "dev": true, "license": "MIT" }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -6098,6 +6269,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", + "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", @@ -6818,6 +6996,16 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -9832,6 +10020,13 @@ "node": ">=8" } }, + "node_modules/loupe": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.2.tgz", + "integrity": "sha512-23I4pFZHmAemUnz8WZXbYRSKYj801VDaNv9ETuMh7IrMc7VuVVSo+Z9iLE3ni30+U48iDWfi30d3twAXBYmnCg==", + "dev": true, + "license": "MIT" + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -9876,9 +10071,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.11", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", - "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, "license": "MIT", "dependencies": { @@ -11278,6 +11473,23 @@ "node": ">=4" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/periscopic": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", @@ -12587,6 +12799,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -12800,6 +13019,20 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "dev": true, + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", @@ -13475,6 +13708,50 @@ "globrex": "^0.1.2" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -13993,6 +14270,29 @@ } } }, + "node_modules/vite-node": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.8.tgz", + "integrity": "sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite-plugin-singlefile": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.1.0.tgz", @@ -14455,6 +14755,72 @@ } } }, + "node_modules/vitest": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.8.tgz", + "integrity": "sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.8", + "@vitest/mocker": "2.1.8", + "@vitest/pretty-format": "^2.1.8", + "@vitest/runner": "2.1.8", + "@vitest/snapshot": "2.1.8", + "@vitest/spy": "2.1.8", + "@vitest/utils": "2.1.8", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.8", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.8", + "@vitest/ui": "2.1.8", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -14553,6 +14919,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 51a2a785..a4f5f0b7 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,8 @@ "typescript": "^5.7.3", "typescript-eslint": "^8.19.1", "vite": "^5.4.11", - "vite-plugin-singlefile": "^2.1.0" + "vite-plugin-singlefile": "^2.1.0", + "vitest": "^2.1.8" }, "scripts": { "clean": "rimraf dist/*", @@ -72,11 +73,12 @@ "lint:ts:typecheck:backend": "tsc --noEmit --project backend.tsconfig.json", "lint:ts:typecheck:frontend": "tsc --noEmit --project frontend.tsconfig.json", "lint:ts:typecheck": "run-p -c --aggregate-output lint:ts:typecheck:*", - "lint:ts:eslint": "eslint --color \"src/**/*.svelte\" \"src/**/*.ts\" \"__tests__/**/*.ts\" \"*.config.{js,ts}\"", + "lint:ts:eslint": "eslint --color \"src/**/*.svelte\" \"src/**/*.ts\" \"tests/**/*.ts\" \"__tests__/**/*.ts\" \"*.config.{js,ts}\"", "lint:ts": "run-p -c --aggregate-output lint:ts:*", "lint": "run-p -c --aggregate-output lint:*", "start": "vite --config frontend.vite.config.ts", "test:backend": "jest", + "vitest:backend": "vitest --config backend.vite.config.ts", "test:frontend": "nyc --reporter=lcov playwright test", "test": "run-p -c test:*", "playwright-interactive": "playwright test --ui" diff --git a/__tests__/backend/doGet.test.ts b/tests/backend/doGet.test.ts similarity index 59% rename from __tests__/backend/doGet.test.ts rename to tests/backend/doGet.test.ts index f6031bde..17daf5d3 100644 --- a/__tests__/backend/doGet.test.ts +++ b/tests/backend/doGet.test.ts @@ -1,5 +1,4 @@ -import { expect, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import { doGet } from "../../src/backend/doGet"; import { @@ -11,14 +10,16 @@ import { test("doGet works correctly", () => { const outputWithTitle = mockedHtmlOutput(); const outputWithoutTitle = mockedHtmlOutput(); - const setTitle = - mocked(outputWithoutTitle).setTitle.mockReturnValueOnce(outputWithTitle); + const setTitle = vi + .mocked(outputWithoutTitle) + .setTitle.mockReturnValueOnce(outputWithTitle); global.HtmlService = mockedHtmlService(); const htmlTemplate = mockedHtmlTemplate(); - mocked(htmlTemplate).evaluate.mockReturnValueOnce(outputWithoutTitle); - const createTemplateFromFile = mocked( - global.HtmlService, - ).createTemplateFromFile.mockReturnValueOnce(htmlTemplate); + // eslint-disable-next-line @typescript-eslint/unbound-method -- vi.mocked is just a type wrapper + vi.mocked(htmlTemplate.evaluate).mockReturnValueOnce(outputWithoutTitle); + const createTemplateFromFile = vi + .mocked(global.HtmlService) + .createTemplateFromFile.mockReturnValueOnce(htmlTemplate); expect(doGet()).toBe(outputWithTitle); expect(createTemplateFromFile.mock.calls).toHaveLength(1); diff --git a/tests/test-utils/gas-stubs.ts b/tests/test-utils/gas-stubs.ts new file mode 100644 index 00000000..d89fabce --- /dev/null +++ b/tests/test-utils/gas-stubs.ts @@ -0,0 +1,462 @@ +import { vi } from "vitest"; + +export function mockedCommentsCollection(): GoogleAppsScript.Drive.Collection.CommentsCollection { + return { + get: vi.fn< + ( + fileId: string, + commentId: string, + ) => GoogleAppsScript.Drive.Schema.Comment + >(), + insert: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Comment, + fileId: string, + ) => GoogleAppsScript.Drive.Schema.Comment + >(), + list: vi.fn< + (fileId: string) => GoogleAppsScript.Drive.Schema.CommentList + >(), + patch: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Comment, + fileId: string, + commentId: string, + ) => GoogleAppsScript.Drive.Schema.Comment + >(), + remove: vi.fn<(fileId: string, commentId: string) => void>(), + update: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Comment, + fileId: string, + commentId: string, + ) => GoogleAppsScript.Drive.Schema.Comment + >(), + }; +} + +export function mockedDrive(): GoogleAppsScript.Drive_v2 { + return { + newChannel: vi.fn<() => GoogleAppsScript.Drive.Schema.Channel>(), + newChildReference: + vi.fn<() => GoogleAppsScript.Drive.Schema.ChildReference>(), + newComment: vi.fn<() => GoogleAppsScript.Drive.Schema.Comment>(), + newCommentContext: + vi.fn<() => GoogleAppsScript.Drive.Schema.CommentContext>(), + newCommentReply: vi.fn<() => GoogleAppsScript.Drive.Schema.CommentReply>(), + newDrive: vi.fn<() => GoogleAppsScript.Drive.Schema.Drive>(), + newDriveBackgroundImageFile: + vi.fn<() => GoogleAppsScript.Drive.Schema.DriveBackgroundImageFile>(), + newDriveCapabilities: + vi.fn<() => GoogleAppsScript.Drive.Schema.DriveCapabilities>(), + newDriveRestrictions: + vi.fn<() => GoogleAppsScript.Drive.Schema.DriveRestrictions>(), + newFile: vi.fn<() => GoogleAppsScript.Drive.Schema.File>(), + newFileCapabilities: + vi.fn<() => GoogleAppsScript.Drive.Schema.FileCapabilities>(), + newFileImageMediaMetadata: + vi.fn<() => GoogleAppsScript.Drive.Schema.FileImageMediaMetadata>(), + newFileImageMediaMetadataLocation: + vi.fn< + () => GoogleAppsScript.Drive.Schema.FileImageMediaMetadataLocation + >(), + newFileIndexableText: + vi.fn<() => GoogleAppsScript.Drive.Schema.FileIndexableText>(), + newFileLabels: vi.fn<() => GoogleAppsScript.Drive.Schema.FileLabels>(), + newFileThumbnail: + vi.fn<() => GoogleAppsScript.Drive.Schema.FileThumbnail>(), + newFileVideoMediaMetadata: + vi.fn<() => GoogleAppsScript.Drive.Schema.FileVideoMediaMetadata>(), + newParentReference: + vi.fn<() => GoogleAppsScript.Drive.Schema.ParentReference>(), + newPermission: vi.fn<() => GoogleAppsScript.Drive.Schema.Permission>(), + newPermissionPermissionDetails: + vi.fn<() => GoogleAppsScript.Drive.Schema.PermissionPermissionDetails>(), + newPermissionTeamDrivePermissionDetails: + vi.fn< + () => GoogleAppsScript.Drive.Schema.PermissionTeamDrivePermissionDetails + >(), + newProperty: vi.fn<() => GoogleAppsScript.Drive.Schema.Property>(), + newRevision: vi.fn<() => GoogleAppsScript.Drive.Schema.Revision>(), + newTeamDrive: vi.fn<() => GoogleAppsScript.Drive.Schema.TeamDrive>(), + newTeamDriveBackgroundImageFile: + vi.fn<() => GoogleAppsScript.Drive.Schema.TeamDriveBackgroundImageFile>(), + newTeamDriveCapabilities: + vi.fn<() => GoogleAppsScript.Drive.Schema.TeamDriveCapabilities>(), + newTeamDriveRestrictions: + vi.fn<() => GoogleAppsScript.Drive.Schema.TeamDriveRestrictions>(), + newUser: vi.fn<() => GoogleAppsScript.Drive.Schema.User>(), + newUserPicture: vi.fn<() => GoogleAppsScript.Drive.Schema.UserPicture>(), + }; +} + +export function mockedDrivesCollection(): GoogleAppsScript.Drive.Collection.DrivesCollection { + return { + get: vi.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), + hide: vi.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), + insert: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Drive, + requestId: string, + ) => GoogleAppsScript.Drive.Schema.Drive + >(), + list: vi.fn<() => GoogleAppsScript.Drive.Schema.DriveList>(), + remove: vi.fn<(driveId: string) => void>(), + unhide: vi.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), + update: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Drive, + driveId: string, + ) => GoogleAppsScript.Drive.Schema.Drive + >(), + }; +} + +export function mockedFilesCollection(): GoogleAppsScript.Drive.Collection.FilesCollection { + return { + copy: vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fileId: string, + ) => GoogleAppsScript.Drive.Schema.File + >(), + emptyTrash: vi.fn<() => void>(), + export: vi.fn<(fileId: string, mimeType: string) => void>(), + generateIds: vi.fn<() => GoogleAppsScript.Drive.Schema.GeneratedIds>(), + get: vi.fn() as { + ( + fileId: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From upstream types + optionalArgs?: Record & { alt: "media" }, + ): string; + ( + fileId: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From upstream types + optionalArgs?: Record, + ): GoogleAppsScript.Drive.Schema.File; + }, + insert: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + mediaData?: Blob, + ) => GoogleAppsScript.Drive.Schema.File + >(), + list: vi.fn<() => GoogleAppsScript.Drive.Schema.FileList>(), + patch: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fileId: string, + ) => GoogleAppsScript.Drive.Schema.File + >(), + remove: vi.fn<(fileId: string) => void>(), + touch: vi.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), + trash: vi.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), + untrash: vi.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), + update: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fileId: string, + mediaData?: Blob, + ) => GoogleAppsScript.Drive.Schema.File + >(), + watch: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Channel, + fileId: string, + ) => GoogleAppsScript.Drive.Schema.Channel + >(), + }; +} + +export function mockedHtmlOutput(): GoogleAppsScript.HTML.HtmlOutput { + return { + addMetaTag: + vi.fn< + (name: string, content: string) => GoogleAppsScript.HTML.HtmlOutput + >(), + append: vi.fn<(addedContent: string) => GoogleAppsScript.HTML.HtmlOutput>(), + appendUntrusted: + vi.fn<(addedContent: string) => GoogleAppsScript.HTML.HtmlOutput>(), + asTemplate: vi.fn<() => GoogleAppsScript.HTML.HtmlTemplate>(), + clear: vi.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), + getAs: vi.fn<(contentType: string) => GoogleAppsScript.Base.Blob>(), + getBlob: vi.fn<() => GoogleAppsScript.Base.Blob>(), + getContent: vi.fn<() => string>(), + getFaviconUrl: vi.fn<() => string>(), + getHeight: vi.fn<() => GoogleAppsScript.Integer>(), + getMetaTags: vi.fn<() => Array>(), + getTitle: vi.fn<() => string>(), + getWidth: vi.fn<() => GoogleAppsScript.Integer>(), + setContent: vi.fn<(content: string) => GoogleAppsScript.HTML.HtmlOutput>(), + setFaviconUrl: + vi.fn<(iconUrl: string) => GoogleAppsScript.HTML.HtmlOutput>(), + setHeight: + vi.fn< + (height: GoogleAppsScript.Integer) => GoogleAppsScript.HTML.HtmlOutput + >(), + setSandboxMode: + vi.fn< + ( + mode: GoogleAppsScript.HTML.SandboxMode, + ) => GoogleAppsScript.HTML.HtmlOutput + >(), + setTitle: vi.fn<(title: string) => GoogleAppsScript.HTML.HtmlOutput>(), + setWidth: + vi.fn< + (width: GoogleAppsScript.Integer) => GoogleAppsScript.HTML.HtmlOutput + >(), + setXFrameOptionsMode: + vi.fn< + ( + mode: GoogleAppsScript.HTML.XFrameOptionsMode, + ) => GoogleAppsScript.HTML.HtmlOutput + >(), + }; +} + +export function mockedHtmlService(): GoogleAppsScript.HTML.HtmlService { + return { + createHtmlOutput: vi.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), + createHtmlOutputFromFile: + vi.fn<(filename: string) => GoogleAppsScript.HTML.HtmlOutput>(), + createTemplate: + vi.fn< + ( + arg: GoogleAppsScript.Base.BlobSource | string, + ) => GoogleAppsScript.HTML.HtmlTemplate + >(), + createTemplateFromFile: vi.fn<() => GoogleAppsScript.HTML.HtmlTemplate>(), + getUserAgent: vi.fn<() => string>(), + SandboxMode: 0 as unknown as typeof GoogleAppsScript.HTML.SandboxMode, + XFrameOptionsMode: + 0 as unknown as typeof GoogleAppsScript.HTML.XFrameOptionsMode, + }; +} + +export function mockedHtmlTemplate(): GoogleAppsScript.HTML.HtmlTemplate { + return { + evaluate: vi.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), + getCode: vi.fn<() => string>(), + getCodeWithComments: vi.fn<() => string>(), + getRawContent: vi.fn<() => string>(), + }; +} + +export function mockedRepliesCollection(): GoogleAppsScript.Drive.Collection.RepliesCollection { + return { + get: vi.fn< + ( + fileId: string, + commentId: string, + replyId: string, + ) => GoogleAppsScript.Drive.Schema.CommentReply + >(), + insert: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.CommentReply, + fileId: string, + commentId: string, + ) => GoogleAppsScript.Drive.Schema.CommentReply + >(), + list: vi.fn< + ( + fileId: string, + commentId: string, + ) => GoogleAppsScript.Drive.Schema.CommentReplyList + >(), + patch: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.CommentReply, + fileId: string, + commentId: string, + replyId: string, + ) => GoogleAppsScript.Drive.Schema.CommentReply + >(), + remove: + vi.fn<(fileId: string, commentId: string, replyId: string) => void>(), + update: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.CommentReply, + fileId: string, + commentId: string, + replyId: string, + ) => GoogleAppsScript.Drive.Schema.CommentReply + >(), + }; +} + +export function mockedSession(): GoogleAppsScript.Base.Session { + return { + getActiveUser: vi.fn<() => GoogleAppsScript.Base.User>(), + getActiveUserLocale: vi.fn<() => string>(), + getEffectiveUser: vi.fn<() => GoogleAppsScript.Base.User>(), + getScriptTimeZone: vi.fn<() => string>(), + getTemporaryActiveUserKey: vi.fn<() => string>(), + getTimeZone: vi.fn<() => string>(), + getUser: vi.fn<() => GoogleAppsScript.Base.User>(), + }; +} + +export function mockedUtilities(): GoogleAppsScript.Utilities.Utilities { + return { + base64Decode: + vi.fn< + ( + encoded: string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + base64DecodeWebSafe: + vi.fn< + ( + encoded: string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + base64Encode: + vi.fn< + ( + data: Array | string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => string + >(), + base64EncodeWebSafe: + vi.fn< + ( + data: Array | string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => string + >(), + Charset: { US_ASCII: 0, UTF_8: 1 }, + computeDigest: + vi.fn< + ( + algorithm: GoogleAppsScript.Utilities.DigestAlgorithm, + value: Array | string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + computeHmacSha256Signature: + vi.fn< + ( + value: Array | string, + key: Array | string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + computeHmacSignature: + vi.fn< + ( + algorithm: GoogleAppsScript.Utilities.MacAlgorithm, + value: Array | string, + key: Array | string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + computeRsaSha1Signature: + vi.fn< + ( + value: string, + key: string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + computeRsaSha256Signature: + vi.fn< + ( + value: string, + key: string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + computeRsaSignature: + vi.fn< + ( + algorithm: GoogleAppsScript.Utilities.RsaAlgorithm, + value: string, + key: string, + charset?: GoogleAppsScript.Utilities.Charset, + ) => Array + >(), + DigestAlgorithm: { + MD2: 0, + MD5: 1, + SHA_1: 2, + SHA_256: 3, + SHA_384: 4, + SHA_512: 5, + }, + formatDate: + vi.fn< + ( + date: GoogleAppsScript.Base.Date, + timeZone: string, + format: string, + ) => string + >(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types + formatString: vi.fn<(template: string, ...args: Array) => string>(), + getUuid: vi.fn<() => string>(), + gzip: vi.fn< + ( + blob: GoogleAppsScript.Base.BlobSource, + name?: string, + ) => GoogleAppsScript.Base.Blob + >(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types + jsonParse: vi.fn<(jsonString: string) => any>(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types + jsonStringify: vi.fn<(obj: any) => string>(), + MacAlgorithm: { + HMAC_MD5: 0, + HMAC_SHA_1: 1, + HMAC_SHA_256: 2, + HMAC_SHA_384: 3, + HMAC_SHA_512: 4, + }, + newBlob: + vi.fn< + ( + data: Array | string, + contentType?: string, + name?: string, + ) => GoogleAppsScript.Base.Blob + >(), + parseCsv: + vi.fn< + (csv: string, delimiter?: GoogleAppsScript.Char) => Array> + >(), + parseDate: + vi.fn<(date: string, timeZone: string, format: string) => Date>(), + RsaAlgorithm: { RSA_SHA_1: 0, RSA_SHA_256: 1 }, + sleep: vi.fn<(milliseconds: GoogleAppsScript.Integer) => void>(), + ungzip: + vi.fn< + (blob: GoogleAppsScript.Base.BlobSource) => GoogleAppsScript.Base.Blob + >(), + unzip: + vi.fn< + ( + blob: GoogleAppsScript.Base.BlobSource, + ) => Array + >(), + zip: vi.fn< + ( + blobs: Array, + name?: string, + ) => GoogleAppsScript.Base.Blob + >(), + }; +} From 523419f9ea8cb2f54ad745d9f195da9ee3f2f912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 14:19:08 +0100 Subject: [PATCH 03/23] Added vitest linting --- eslint.config.js | 49 +++++++++++++++++++++++++++++++++++++ package-lock.json | 22 +++++++++++++++++ package.json | 1 + tests/backend/doGet.test.ts | 1 - 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/eslint.config.js b/eslint.config.js index cbfb85ce..6138070e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,6 +1,7 @@ import eslintComments from "@eslint-community/eslint-plugin-eslint-comments"; import commentsConfig from "@eslint-community/eslint-plugin-eslint-comments/configs"; import js from "@eslint/js"; +import vitest from "@vitest/eslint-plugin"; import jest from "eslint-plugin-jest"; import perfectionist from "eslint-plugin-perfectionist"; import playwright from "eslint-plugin-playwright"; @@ -218,6 +219,54 @@ export default tseslint.config( }, { files: ["tests/**/*.test.ts", "tests/test-utils/gas-stubs.ts"], + ...vitest.configs.recommended, + rules: { + ...vitest.configs.recommended.rules, + "@typescript-eslint/unbound-method": "off", + "vitest/consistent-test-it": ["error", { withinDescribe: "test" }], + "vitest/no-alias-methods": "error", + "vitest/no-conditional-expect": "error", + "vitest/no-conditional-in-test": "error", + "vitest/no-conditional-tests": "error", + "vitest/no-disabled-tests": "error", + "vitest/no-duplicate-hooks": "error", + "vitest/no-focused-tests": "error", + "vitest/no-interpolation-in-snapshots": "error", + "vitest/no-large-snapshots": "error", + "vitest/no-mocks-import": "error", + "vitest/no-standalone-expect": "error", + "vitest/no-test-prefixes": "error", + "vitest/no-test-return-statement": "error", + "vitest/padding-around-all": "error", + "vitest/prefer-called-with": "error", + "vitest/prefer-comparison-matcher": "error", + "vitest/prefer-each": "error", + "vitest/prefer-equality-matcher": "error", + "vitest/prefer-expect-assertions": [ + "error", + { + onlyFunctionsWithAsyncKeyword: true, + onlyFunctionsWithExpectInCallback: true, + onlyFunctionsWithExpectInLoop: true, + }, + ], + "vitest/prefer-expect-resolves": "error", + "vitest/prefer-hooks-in-order": "error", + "vitest/prefer-hooks-on-top": "error", + "vitest/prefer-mock-promise-shorthand": "error", + "vitest/prefer-snapshot-hint": "error", + "vitest/prefer-spy-on": "error", + "vitest/prefer-strict-equal": "error", + "vitest/prefer-to-be": "error", + "vitest/prefer-to-be-object": "error", + "vitest/prefer-to-contain": "error", + "vitest/prefer-to-have-length": "error", + "vitest/prefer-todo": "error", + "vitest/prefer-vi-mocked": "error", + "vitest/require-hook": "error", + "vitest/require-to-throw-message": "error", + "vitest/valid-expect-in-promise": "error", + }, }, { ...jest.configs["flat/recommended"], diff --git a/package-lock.json b/package-lock.json index 35db6e01..dc63cfbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@types/google.script.client-side": "^0.1.5", "@types/jest": "^29.5.14", "@types/node": "^22.10.2", + "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.9.0", @@ -4530,6 +4531,27 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitest/eslint-plugin": { + "version": "1.1.25", + "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.25.tgz", + "integrity": "sha512-u8DpDnMbPcqBmJOB4PeEtn6q7vKmLVTLFMpzoxSAo0hjYdl4iYSHRleqwPQo0ywc7UV0S6RKIahYRQ3BnZdMVw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@typescript-eslint/utils": ">= 8.0", + "eslint": ">= 8.57.0", + "typescript": ">= 5.0.0", + "vitest": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, "node_modules/@vitest/expect": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.8.tgz", diff --git a/package.json b/package.json index a4f5f0b7..ee293c4c 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@types/google.script.client-side": "^0.1.5", "@types/jest": "^29.5.14", "@types/node": "^22.10.2", + "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jest": "^28.9.0", diff --git a/tests/backend/doGet.test.ts b/tests/backend/doGet.test.ts index 17daf5d3..b813f0d9 100644 --- a/tests/backend/doGet.test.ts +++ b/tests/backend/doGet.test.ts @@ -15,7 +15,6 @@ test("doGet works correctly", () => { .setTitle.mockReturnValueOnce(outputWithTitle); global.HtmlService = mockedHtmlService(); const htmlTemplate = mockedHtmlTemplate(); - // eslint-disable-next-line @typescript-eslint/unbound-method -- vi.mocked is just a type wrapper vi.mocked(htmlTemplate.evaluate).mockReturnValueOnce(outputWithoutTitle); const createTemplateFromFile = vi .mocked(global.HtmlService) From bfeb5c2b571153564dfbc80a7deb6c6e43039258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 14:57:16 +0100 Subject: [PATCH 04/23] Migrated listFolders test to vitest --- __tests__/backend/listFolders.test.ts | 177 ------------------- tests/backend/listFolders.test.ts | 198 ++++++++++++++++++++++ tests/test-utils/SafeDriveService-stub.ts | 102 +++++++++++ 3 files changed, 300 insertions(+), 177 deletions(-) delete mode 100644 __tests__/backend/listFolders.test.ts create mode 100644 tests/backend/listFolders.test.ts create mode 100644 tests/test-utils/SafeDriveService-stub.ts diff --git a/__tests__/backend/listFolders.test.ts b/__tests__/backend/listFolders.test.ts deleted file mode 100644 index 812312c6..00000000 --- a/__tests__/backend/listFolders.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; - -import { listFolders } from "../../src/backend/listFolders"; -import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedSession } from "../test-utils/gas-stubs"; -import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; - -jest.mock<{ SafeDriveService_: jest.Mock }>( - "../../src/backend/utils/SafeDriveService", - () => ({ - SafeDriveService_: jest.fn(), - }), -); - -test("listFolders works correctly", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const rawResponse = { - items: [ - { id: "ID1", title: "FOLDER1" }, - { id: "ID2", title: "FOLDER2" }, - ], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - global.Session = mockedSession(); - mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); - - expect(listFolders("ID_PARENT")).toStrictEqual({ - response: [ - { id: "ID1", name: "FOLDER1" }, - { id: "ID2", name: "FOLDER2" }, - ], - status: "success", - }); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("ID_PARENT"); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .pageToken, - ).toBeUndefined(); -}); - -test("listFolders works correctly with shortcuts", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const rawResponse = { - items: [ - { - id: "ID1", - mimeType: "application/vnd.google-apps.shortcut", - shortcutDetails: { - targetId: "TRUE_ID1", - }, - title: "FOLDER1", - }, - { id: "ID2", title: "FOLDER2" }, - ], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - global.Session = mockedSession(); - mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); - - expect(listFolders("ID_PARENT")).toStrictEqual({ - response: [ - { id: "TRUE_ID1", name: "FOLDER1" }, - { id: "ID2", name: "FOLDER2" }, - ], - status: "success", - }); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("ID_PARENT"); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .pageToken, - ).toBeUndefined(); -}); - -test("listFolders handles invalid parameters gracefully", () => { - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockImplementationOnce(() => { - throw new Error(); - }); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - global.Session = mockedSession(); - mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); - - expect(listFolders(42)).toStrictEqual({ - status: "error", - type: "invalidParameter", - }); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(0); -}); - -test("listFolders handles errors in Google Drive API gracefully", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockImplementationOnce(() => { - throw new Error(); - }); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - global.Session = mockedSession(); - mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); - - expect(listFolders("ID_PARENT")).toStrictEqual({ - status: "error", - type: "DriveAPIError", - }); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("ID_PARENT"); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .pageToken, - ).toBeUndefined(); -}); diff --git a/tests/backend/listFolders.test.ts b/tests/backend/listFolders.test.ts new file mode 100644 index 00000000..e5195f33 --- /dev/null +++ b/tests/backend/listFolders.test.ts @@ -0,0 +1,198 @@ +import { expect, test, vi } from "vitest"; + +import { listFolders } from "../../src/backend/listFolders"; +import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; +import { mockedSession } from "../test-utils/gas-stubs"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; + +vi.mock("../../src/backend/utils/SafeDriveService"); + +test("listFolders works correctly", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const rawResponse = { + items: [ + { id: "ID1", title: "FOLDER1" }, + { id: "ID2", title: "FOLDER2" }, + ], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + global.Session = mockedSession(); + vi.mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); + + expect(listFolders("ID_PARENT")).toStrictEqual({ + response: [ + { id: "ID1", name: "FOLDER1" }, + { id: "ID2", name: "FOLDER2" }, + ], + status: "success", + }); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("ID_PARENT"); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).pageToken, + ).toBeUndefined(); +}); + +test("listFolders works correctly with shortcuts", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const rawResponse = { + items: [ + { + id: "ID1", + mimeType: "application/vnd.google-apps.shortcut", + shortcutDetails: { + targetId: "TRUE_ID1", + }, + title: "FOLDER1", + }, + { id: "ID2", title: "FOLDER2" }, + ], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + global.Session = mockedSession(); + vi.mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); + + expect(listFolders("ID_PARENT")).toStrictEqual({ + response: [ + { id: "TRUE_ID1", name: "FOLDER1" }, + { id: "ID2", name: "FOLDER2" }, + ], + status: "success", + }); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("ID_PARENT"); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).pageToken, + ).toBeUndefined(); +}); + +test("listFolders handles invalid parameters gracefully", () => { + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockImplementationOnce(() => { + throw new Error(); + }); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + global.Session = mockedSession(); + vi.mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); + + expect(listFolders(42)).toStrictEqual({ + status: "error", + type: "invalidParameter", + }); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(0); +}); + +test("listFolders handles errors in Google Drive API gracefully", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockImplementationOnce(() => { + throw new Error(); + }); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + global.Session = mockedSession(); + vi.mocked(global.Session).getActiveUserLocale.mockReturnValueOnce("en"); + + expect(listFolders("ID_PARENT")).toStrictEqual({ + status: "error", + type: "DriveAPIError", + }); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("ID_PARENT"); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).pageToken, + ).toBeUndefined(); +}); diff --git a/tests/test-utils/SafeDriveService-stub.ts b/tests/test-utils/SafeDriveService-stub.ts new file mode 100644 index 00000000..5d74fcd2 --- /dev/null +++ b/tests/test-utils/SafeDriveService-stub.ts @@ -0,0 +1,102 @@ +import { type MockedObject, vi } from "vitest"; + +import type { DeepKeyof } from "../../src/backend/utils/DeepKeyof"; +import type { DeepPick } from "../../src/backend/utils/DeepPick"; +import type { + SafeComment, + SafeCommentList, + SafeDriveList, + SafeDriveService_, + SafeFile, + SafeFileList, +} from "../../src/backend/utils/SafeDriveService"; + +import { mockedRepliesCollection } from "./gas-stubs"; + +export function mockedSafeDriveService< + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- No other way to pass F to vi.fn() + F extends DeepKeyof, +>(): MockedObject { + return { + Comments: { + insert: + vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.Comment, + fileId: string, + ) => SafeComment + >(), + list: vi.fn< + (fileId: string, optionalArgs: Record) => SafeCommentList + >(), + }, + Drives: { + list: vi.fn< + ( + fields: F | null, + optionalArgs?: { + maxResults?: number; + orderBy?: string; + pageToken?: string; + }, + ) => SafeDriveList + >(), + }, + Files: { + copy: vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fileId: string, + fields: F | null, + optionalArgs?: { supportsAllDrives?: boolean }, + ) => DeepPick + >(), + get: vi.fn< + ( + fileId: string, + fields: F | null, + optionalArgs?: { alt?: string }, + ) => DeepPick + >(), + insert: vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fields: F | null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Required by the Drive API + mediaData?: any, + optionalArgs?: { + supportsAllDrives?: boolean; + }, + ) => DeepPick + >(), + list: vi.fn< + ( + fields: F | null, + optionalArgs?: { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + }, + ) => SafeFileList + >(), + remove: vi.fn<(fileId: string) => void>(), + update: vi.fn< + ( + resource: GoogleAppsScript.Drive.Schema.File, + fileId: string, + fields: F | null, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Required by the Drive API + mediaData?: any, + optionalArgs?: { + addParents?: string; + removeParents?: string; + supportsAllDrives?: boolean; + }, + ) => DeepPick + >(), + }, + Replies: mockedRepliesCollection(), + } as unknown as MockedObject; +} From bc517128e53cf98814434005aba3193f7719ede8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:00:34 +0100 Subject: [PATCH 05/23] Migrated listSharedDrives test to vitest --- __tests__/backend/listSharedDrives.test.ts | 77 -------------------- tests/backend/listSharedDrives.test.ts | 83 ++++++++++++++++++++++ 2 files changed, 83 insertions(+), 77 deletions(-) delete mode 100644 __tests__/backend/listSharedDrives.test.ts create mode 100644 tests/backend/listSharedDrives.test.ts diff --git a/__tests__/backend/listSharedDrives.test.ts b/__tests__/backend/listSharedDrives.test.ts deleted file mode 100644 index f0107346..00000000 --- a/__tests__/backend/listSharedDrives.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; - -import { listSharedDrives } from "../../src/backend/listSharedDrives"; -import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; - -jest.mock<{ SafeDriveService_: jest.Mock }>( - "../../src/backend/utils/SafeDriveService", - () => ({ - SafeDriveService_: jest.fn(), - }), -); - -test("listSharedDrives works correctly", () => { - interface ListDrivesOptions { - maxResults?: number; - orderBy?: string; - pageToken?: string; - } - - const response = [ - { id: "ID1", name: "DRIVE1" }, - { id: "ID2", name: "DRIVE2" }, - ]; - const rawResponse = { - items: response, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Drives.list.mockReturnValueOnce(rawResponse); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - expect(listSharedDrives()).toStrictEqual({ response, status: "success" }); - - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(driveServiceMock.Drives.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Drives.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Drives.list.mock.calls[0][1] as ListDrivesOptions) - .pageToken, - ).toBeUndefined(); - expect( - (driveServiceMock.Drives.list.mock.calls[0][1] as ListDrivesOptions) - .orderBy, - ).toBe("name"); -}); - -test("listSharedDrives handles Drive API error gracefully", () => { - interface ListDrivesOptions { - maxResults?: number; - orderBy?: string; - pageToken?: string; - } - - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Drives.list.mockImplementationOnce(() => { - throw new Error(); - }); - mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); - - expect(listSharedDrives()).toStrictEqual({ - status: "error", - type: "DriveAPIError", - }); - - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(driveServiceMock.Drives.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Drives.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Drives.list.mock.calls[0][1] as ListDrivesOptions) - .pageToken, - ).toBeUndefined(); - expect( - (driveServiceMock.Drives.list.mock.calls[0][1] as ListDrivesOptions) - .orderBy, - ).toBe("name"); -}); diff --git a/tests/backend/listSharedDrives.test.ts b/tests/backend/listSharedDrives.test.ts new file mode 100644 index 00000000..00f297f4 --- /dev/null +++ b/tests/backend/listSharedDrives.test.ts @@ -0,0 +1,83 @@ +import { expect, test, vi } from "vitest"; + +import { listSharedDrives } from "../../src/backend/listSharedDrives"; +import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; + +vi.mock("../../src/backend/utils/SafeDriveService"); + +test("listSharedDrives works correctly", () => { + interface ListDrivesOptions { + maxResults?: number; + orderBy?: string; + pageToken?: string; + } + + const response = [ + { id: "ID1", name: "DRIVE1" }, + { id: "ID2", name: "DRIVE2" }, + ]; + const rawResponse = { + items: response, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Drives.list).mockReturnValueOnce(rawResponse); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + expect(listSharedDrives()).toStrictEqual({ response, status: "success" }); + + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Drives.list).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Drives.list).mock.calls[0][1], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Drives.list).mock + .calls[0][1] as ListDrivesOptions + ).pageToken, + ).toBeUndefined(); + expect( + ( + vi.mocked(driveServiceMock.Drives.list).mock + .calls[0][1] as ListDrivesOptions + ).orderBy, + ).toBe("name"); +}); + +test("listSharedDrives handles Drive API error gracefully", () => { + interface ListDrivesOptions { + maxResults?: number; + orderBy?: string; + pageToken?: string; + } + + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Drives.list).mockImplementationOnce(() => { + throw new Error(); + }); + vi.mocked(SafeDriveService_).mockReturnValueOnce(driveServiceMock); + + expect(listSharedDrives()).toStrictEqual({ + status: "error", + type: "DriveAPIError", + }); + + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Drives.list).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Drives.list).mock.calls[0][1], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Drives.list).mock + .calls[0][1] as ListDrivesOptions + ).pageToken, + ).toBeUndefined(); + expect( + ( + vi.mocked(driveServiceMock.Drives.list).mock + .calls[0][1] as ListDrivesOptions + ).orderBy, + ).toBe("name"); +}); From 2e86b696c5880ffebafed6b83eb142746cb1910f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:35:34 +0100 Subject: [PATCH 06/23] Migrated DriveBackedValue test to vitest --- .../backend/utils/DriveBackedValue.test.ts | 809 ------------- tests/backend/utils/DriveBackedValue.test.ts | 1068 +++++++++++++++++ 2 files changed, 1068 insertions(+), 809 deletions(-) delete mode 100644 __tests__/backend/utils/DriveBackedValue.test.ts create mode 100644 tests/backend/utils/DriveBackedValue.test.ts diff --git a/__tests__/backend/utils/DriveBackedValue.test.ts b/__tests__/backend/utils/DriveBackedValue.test.ts deleted file mode 100644 index a9adc4cf..00000000 --- a/__tests__/backend/utils/DriveBackedValue.test.ts +++ /dev/null @@ -1,809 +0,0 @@ -import { expect, test } from "@jest/globals"; -import { mocked } from "jest-mock"; - -import { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; -import { mockedUtilities } from "../../test-utils/gas-stubs"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; - -test("DriveBackedValue constructs correctly", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const driveServiceMock = mockedSafeDriveService(); - - expect(() => { - new DriveBackedValue_(key, driveServiceMock); - }).not.toThrow(); -}); - -test("DriveBackedValue saves a value - the folder exists, the value exists", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - mocked(global.Utilities).newBlob.mockReturnValueOnce( - "BLOB" as unknown as GoogleAppsScript.Base.Blob, - ); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [ - { - id: "FILE_ID", - }, - ], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.saveValue("VALUE"); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(0); - expect(driveServiceMock.Files.update.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.update.mock.calls[0][1]).toBe("FILE_ID"); - expect(driveServiceMock.Files.update.mock.calls[0][3]).toBe("BLOB"); - expect(mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); - expect(mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( - JSON.stringify("VALUE"), - ); - expect(mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( - "application/json", - ); -}); - -test("DriveBackedValue saves a value - the folder exists, the value doesn't", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - mocked(global.Utilities).newBlob.mockReturnValueOnce( - "BLOB" as unknown as GoogleAppsScript.Base.Blob, - ); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.saveValue("VALUE"); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/json", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].parents).toStrictEqual([ - { id: "FOLDER_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( - `shared-drive-mover-state-${keySha256}.json`, - ); - expect(driveServiceMock.Files.insert.mock.calls[0][2]).toBe("BLOB"); - expect(mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); - expect(mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( - JSON.stringify("VALUE"), - ); - expect(mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( - "application/json", - ); -}); - -test("DriveBackedValue saves a value - the folder doesn't exists", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - mocked(global.Utilities).newBlob.mockReturnValueOnce( - "BLOB" as unknown as GoogleAppsScript.Base.Blob, - ); - const response = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.insert.mockReturnValueOnce({ id: "FOLDER_ID" }); - driveServiceMock.Files.list - .mockReturnValueOnce(response) - .mockReturnValueOnce(response); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.saveValue("VALUE"); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/vnd.google-apps.folder", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( - "Shared drive mover cache", - ); - expect(driveServiceMock.Files.insert.mock.calls[1][0].mimeType).toBe( - "application/json", - ); - expect(driveServiceMock.Files.insert.mock.calls[1][0].parents).toStrictEqual([ - { id: "FOLDER_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[1][0].title).toBe( - `shared-drive-mover-state-${keySha256}.json`, - ); - expect(driveServiceMock.Files.insert.mock.calls[1][2]).toBe("BLOB"); - expect(mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); - expect(mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( - JSON.stringify("VALUE"), - ); - expect(mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( - "application/json", - ); -}); - -test("DriveBackedValue loads a value - the folder exists, the value exists", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - interface GetFileOptions { - alt?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [ - { - id: "FILE_ID", - }, - ], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.get.mockReturnValueOnce(JSON.stringify("VALUE")); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - - expect(driveBackedValue.loadValue()).toBe("VALUE"); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.get.mock.calls[0][0]).toBe("FILE_ID"); - expect(driveServiceMock.Files.get.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.get.mock.calls[0][2] as GetFileOptions).alt, - ).toBe("media"); -}); - -test("DriveBackedValue loads a value - the folder exists, the value doesn't", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - - expect(driveBackedValue.loadValue()).toBeNull(); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(0); -}); - -test("DriveBackedValue loads a value - the folder doesn't exist", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(response1); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - - expect(driveBackedValue.loadValue()).toBeNull(); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(0); -}); - -test("DriveBackedValue deletes a value - the folder exists, the value exists, the folder doesn't contain other files", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [ - { - id: "FILE_ID", - }, - ], - }; - const response3 = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2) - .mockReturnValueOnce(response3); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.deleteValue(); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(3); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[2][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(2); - expect(driveServiceMock.Files.remove.mock.calls[0][0]).toBe("FILE_ID"); - expect(driveServiceMock.Files.remove.mock.calls[1][0]).toBe("FOLDER_ID"); -}); - -test("DriveBackedValue deletes a value - the folder exists, the value exists, the folder contains other files", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [ - { - id: "FILE_ID", - }, - ], - }; - const response3 = { - items: [ - { - id: "OTHER_FILE_ID", - }, - ], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2) - .mockReturnValueOnce(response3); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.deleteValue(); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(3); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[2][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.remove.mock.calls[0][0]).toBe("FILE_ID"); -}); - -test("DriveBackedValue deletes a value - the folder exists, the value doesn't, the folder doesn't contain other files", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [], - }; - const response3 = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2) - .mockReturnValueOnce(response3); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.deleteValue(); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(3); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[2][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.remove.mock.calls[0][0]).toBe("FOLDER_ID"); -}); - -test("DriveBackedValue deletes a value - the folder exists, the value doesn't, the folder contains other files", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - const keySha256 = - "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response1 = { - items: [ - { - id: "FOLDER_ID", - }, - ], - }; - const response2 = { - items: [], - }; - const response3 = { - items: [ - { - id: "OTHER_FILE_ID", - }, - ], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list - .mockReturnValueOnce(response1) - .mockReturnValueOnce(response2) - .mockReturnValueOnce(response3); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.deleteValue(); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(3); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[1][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[1][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.list.mock.calls[2][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[2][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(0); -}); - -test("DriveBackedValue deletes a value - the folder doesn't exist", () => { - const key = "SAVE_KEY"; - const keyEncoded = [ - -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, - -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, - 16, - ]; - - interface ListFilesOptions { - fields?: string; - maxResults?: number; - q?: string; - } - - global.Utilities = mockedUtilities(); - mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); - const response = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(response); - - const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); - driveBackedValue.deleteValue(); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('title = "Shared drive mover cache"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"root" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("trashed = false"); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(0); -}); diff --git a/tests/backend/utils/DriveBackedValue.test.ts b/tests/backend/utils/DriveBackedValue.test.ts new file mode 100644 index 00000000..8a7d60bd --- /dev/null +++ b/tests/backend/utils/DriveBackedValue.test.ts @@ -0,0 +1,1068 @@ +import { expect, test, vi } from "vitest"; + +import { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; +import { mockedUtilities } from "../../test-utils/gas-stubs"; +import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; + +test("DriveBackedValue constructs correctly", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const driveServiceMock = mockedSafeDriveService(); + + expect(() => { + new DriveBackedValue_(key, driveServiceMock); + }).not.toThrow(); +}); + +test("DriveBackedValue saves a value - the folder exists, the value exists", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + vi.mocked(global.Utilities).newBlob.mockReturnValueOnce( + "BLOB" as unknown as GoogleAppsScript.Base.Blob, + ); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [ + { + id: "FILE_ID", + }, + ], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.saveValue("VALUE"); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(0); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls[0][1]).toBe( + "FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls[0][3]).toBe( + "BLOB", + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( + JSON.stringify("VALUE"), + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( + "application/json", + ); +}); + +test("DriveBackedValue saves a value - the folder exists, the value doesn't", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + vi.mocked(global.Utilities).newBlob.mockReturnValueOnce( + "BLOB" as unknown as GoogleAppsScript.Base.Blob, + ); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.saveValue("VALUE"); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/json"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].parents, + ).toStrictEqual([{ id: "FOLDER_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( + `shared-drive-mover-state-${keySha256}.json`, + ); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][2]).toBe( + "BLOB", + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( + JSON.stringify("VALUE"), + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( + "application/json", + ); +}); + +test("DriveBackedValue saves a value - the folder doesn't exists", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + vi.mocked(global.Utilities).newBlob.mockReturnValueOnce( + "BLOB" as unknown as GoogleAppsScript.Base.Blob, + ); + const response = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.insert).mockReturnValueOnce({ + id: "FOLDER_ID", + }); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response) + .mockReturnValueOnce(response); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.saveValue("VALUE"); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(2); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/vnd.google-apps.folder"); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( + "Shared drive mover cache", + ); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[1][0].mimeType, + ).toBe("application/json"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[1][0].parents, + ).toStrictEqual([{ id: "FOLDER_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[1][0].title).toBe( + `shared-drive-mover-state-${keySha256}.json`, + ); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[1][2]).toBe( + "BLOB", + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls).toHaveLength(1); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][0]).toBe( + JSON.stringify("VALUE"), + ); + expect(vi.mocked(global.Utilities).newBlob.mock.calls[0][1]).toBe( + "application/json", + ); +}); + +test("DriveBackedValue loads a value - the folder exists, the value exists", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + interface GetFileOptions { + alt?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [ + { + id: "FILE_ID", + }, + ], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.get).mockReturnValueOnce( + JSON.stringify("VALUE"), + ); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + + expect(driveBackedValue.loadValue()).toBe("VALUE"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls[0][0]).toBe( + "FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls[0][1]).toBeDefined(); + expect( + (vi.mocked(driveServiceMock.Files.get).mock.calls[0][2] as GetFileOptions) + .alt, + ).toBe("media"); +}); + +test("DriveBackedValue loads a value - the folder exists, the value doesn't", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + + expect(driveBackedValue.loadValue()).toBeNull(); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(0); +}); + +test("DriveBackedValue loads a value - the folder doesn't exist", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(response1); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + + expect(driveBackedValue.loadValue()).toBeNull(); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(0); +}); + +test("DriveBackedValue deletes a value - the folder exists, the value exists, the folder doesn't contain other files", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [ + { + id: "FILE_ID", + }, + ], + }; + const response3 = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2) + .mockReturnValueOnce(response3); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.deleteValue(); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(3); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[2][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(2); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls[0][0]).toBe( + "FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls[1][0]).toBe( + "FOLDER_ID", + ); +}); + +test("DriveBackedValue deletes a value - the folder exists, the value exists, the folder contains other files", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [ + { + id: "FILE_ID", + }, + ], + }; + const response3 = { + items: [ + { + id: "OTHER_FILE_ID", + }, + ], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2) + .mockReturnValueOnce(response3); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.deleteValue(); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(3); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[2][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls[0][0]).toBe( + "FILE_ID", + ); +}); + +test("DriveBackedValue deletes a value - the folder exists, the value doesn't, the folder doesn't contain other files", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [], + }; + const response3 = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2) + .mockReturnValueOnce(response3); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.deleteValue(); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(3); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[2][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls[0][0]).toBe( + "FOLDER_ID", + ); +}); + +test("DriveBackedValue deletes a value - the folder exists, the value doesn't, the folder contains other files", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + const keySha256 = + "b4cbb0ac37bd7e93d1cd87f020dab1ac2775a402719b4c517f5d6d68748d2510"; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response1 = { + items: [ + { + id: "FOLDER_ID", + }, + ], + }; + const response2 = { + items: [], + }; + const response3 = { + items: [ + { + id: "OTHER_FILE_ID", + }, + ], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list) + .mockReturnValueOnce(response1) + .mockReturnValueOnce(response2) + .mockReturnValueOnce(response3); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.deleteValue(); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(3); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[1][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain(`title = "shared-drive-mover-state-${keySha256}.json"`); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[1][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[2][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[2][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(0); +}); + +test("DriveBackedValue deletes a value - the folder doesn't exist", () => { + const key = "SAVE_KEY"; + const keyEncoded = [ + -76, -53, -80, -84, 55, -67, -130, -109, -47, -51, -121, -16, 32, -38, -79, + -84, 39, 117, -92, 2, 113, -101, 76, 81, -129, 93, 109, 104, 116, -115, 37, + 16, + ]; + + interface ListFilesOptions { + fields?: string; + maxResults?: number; + q?: string; + } + + global.Utilities = mockedUtilities(); + vi.mocked(global.Utilities).computeDigest.mockReturnValueOnce(keyEncoded); + const response = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(response); + + const driveBackedValue = new DriveBackedValue_(key, driveServiceMock); + driveBackedValue.deleteValue(); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][1]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('title = "Shared drive mover cache"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"root" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("trashed = false"); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(0); +}); From e94c709634e153cd5eca25af3f51d726e23eb5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:38:21 +0100 Subject: [PATCH 07/23] Migrated MoveState test to vitest --- .../backend/utils/MoveState.test.ts | 32 ++++++++----------- tests/test-utils/DriveBackedValue-stub.ts | 23 +++++++++++++ 2 files changed, 36 insertions(+), 19 deletions(-) rename {__tests__ => tests}/backend/utils/MoveState.test.ts (92%) create mode 100644 tests/test-utils/DriveBackedValue-stub.ts diff --git a/__tests__/backend/utils/MoveState.test.ts b/tests/backend/utils/MoveState.test.ts similarity index 92% rename from __tests__/backend/utils/MoveState.test.ts rename to tests/backend/utils/MoveState.test.ts index 1baff1ee..36ec5049 100644 --- a/__tests__/backend/utils/MoveState.test.ts +++ b/tests/backend/utils/MoveState.test.ts @@ -1,22 +1,16 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; import { mockedDriveBackedValue } from "../../test-utils/DriveBackedValue-stub"; import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; -jest.mock<{ DriveBackedValue_: jest.Mock }>( - "../../../src/backend/utils/DriveBackedValue", - () => ({ - DriveBackedValue_: jest.fn(), - }), -); +vi.mock("../../../src/backend/utils/DriveBackedValue"); test("MoveState constructs correctly", () => { const driveBackedValueMock = mockedDriveBackedValue(); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", @@ -26,8 +20,8 @@ test("MoveState constructs correctly", () => { driveServiceMock, ); - expect(mocked(DriveBackedValue_).mock.calls).toHaveLength(1); - expect(mocked(DriveBackedValue_).mock.calls[0][0]).toBe( + expect(vi.mocked(DriveBackedValue_).mock.calls).toHaveLength(1); + expect(vi.mocked(DriveBackedValue_).mock.calls[0][0]).toBe( JSON.stringify({ copyComments: false, destinationID: "DEST_BASE_ID", @@ -230,7 +224,7 @@ test("MoveState.tryOrLog works correctly", () => { driveServiceMock, ); - const fn = jest.fn<() => void>().mockReturnValueOnce(); + const fn = vi.fn<() => void>().mockReturnValueOnce(); state.tryOrLog(context, fn); @@ -275,7 +269,7 @@ test("MoveState.tryOrLog handles errors gracefully", () => { driveServiceMock, ); - const fn = jest.fn<() => void>().mockImplementationOnce(() => { + const fn = vi.fn<() => void>().mockImplementationOnce(() => { throw new Error("ERROR_MESSAGE"); }); @@ -304,7 +298,7 @@ test("MoveState.tryOrLog handles errors gracefully with a filename", () => { driveServiceMock, ); - const fn = jest.fn<() => void>().mockImplementationOnce(() => { + const fn = vi.fn<() => void>().mockImplementationOnce(() => { throw new Error("ERROR_MESSAGE"); }); @@ -321,7 +315,7 @@ test("MoveState.tryOrLog handles errors gracefully with a filename", () => { test("MoveState saves the state correctly", () => { const driveBackedValueMock = mockedDriveBackedValue(); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", @@ -380,7 +374,7 @@ test("MoveState saves the state correctly", () => { test("MoveState doesn't save empty state", () => { const driveBackedValueMock = mockedDriveBackedValue(); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", @@ -414,7 +408,7 @@ test("MoveState loads the state correctly", () => { pathsToProcess: [path], }); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", @@ -435,7 +429,7 @@ test("MoveState handles empty state load correctly", () => { const driveBackedValueMock = mockedDriveBackedValue(); driveBackedValueMock.loadValue.mockReturnValueOnce(null); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", @@ -453,7 +447,7 @@ test("MoveState handles empty state load correctly", () => { test("MoveState destroys state correctly", () => { const driveBackedValueMock = mockedDriveBackedValue(); const driveServiceMock = mockedSafeDriveService(); - mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); + vi.mocked(DriveBackedValue_).mockReturnValue(driveBackedValueMock); const state = new MoveState_( "SRC_BASE_ID", diff --git a/tests/test-utils/DriveBackedValue-stub.ts b/tests/test-utils/DriveBackedValue-stub.ts new file mode 100644 index 00000000..ff4e6410 --- /dev/null +++ b/tests/test-utils/DriveBackedValue-stub.ts @@ -0,0 +1,23 @@ +import { type MockedObject, vi } from "vitest"; + +import type { DriveBackedValue_ } from "../../src/backend/utils/DriveBackedValue"; +import type { MoveContext } from "../../src/interfaces/MoveContext"; +import type { MoveError } from "../../src/interfaces/MoveError"; + +export function mockedDriveBackedValue(): MockedObject< + DriveBackedValue_<{ + errors: Array; + pathsToProcess: Array; + }> +> { + return { + deleteValue: vi.fn(), + loadValue: vi.fn(), + saveValue: vi.fn(), + } as unknown as MockedObject< + DriveBackedValue_<{ + errors: Array; + pathsToProcess: Array; + }> + >; +} From e7d4b156e1a250db2ae7872cf88b35bebfbe9522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:39:40 +0100 Subject: [PATCH 08/23] Migrated paginationHelper test to vitest --- .../backend/utils/paginationHelper.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) rename {__tests__ => tests}/backend/utils/paginationHelper.test.ts (93%) diff --git a/__tests__/backend/utils/paginationHelper.test.ts b/tests/backend/utils/paginationHelper.test.ts similarity index 93% rename from __tests__/backend/utils/paginationHelper.test.ts rename to tests/backend/utils/paginationHelper.test.ts index 1656f2be..9ee9ce36 100644 --- a/__tests__/backend/utils/paginationHelper.test.ts +++ b/tests/backend/utils/paginationHelper.test.ts @@ -1,4 +1,4 @@ -import { expect, jest, test } from "@jest/globals"; +import { expect, test, vi } from "vitest"; import { paginationHelper_ } from "../../../src/backend/utils/paginationHelper"; @@ -11,10 +11,10 @@ test("paginationHelper works correctly", () => { a: "b", }; - const request = jest + const request = vi .fn<(pageToken: string | undefined) => T>() .mockReturnValueOnce(rawResponse); - const transform = jest + const transform = vi .fn<(response: T) => Array>() .mockReturnValueOnce(["first", "second"]); @@ -45,12 +45,12 @@ test("paginationHelper works correctly with multiple pages", () => { a: "c", }; - const request = jest + const request = vi .fn<(pageToken: string | undefined) => T>() .mockReturnValueOnce(rawResponse1) .mockReturnValueOnce(rawResponse2) .mockReturnValueOnce(rawResponse3); - const transform = jest + const transform = vi .fn<(response: T) => Array>() .mockReturnValueOnce(["first", "second"]) .mockReturnValueOnce(["third", "fourth"]) From 944df082a62390c110c000f2989399223bc3dbe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:40:25 +0100 Subject: [PATCH 09/23] Migrated SafeDriveService test to vitest --- {__tests__ => tests}/backend/utils/SafeDriveService.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {__tests__ => tests}/backend/utils/SafeDriveService.test.ts (97%) diff --git a/__tests__/backend/utils/SafeDriveService.test.ts b/tests/backend/utils/SafeDriveService.test.ts similarity index 97% rename from __tests__/backend/utils/SafeDriveService.test.ts rename to tests/backend/utils/SafeDriveService.test.ts index c30b745d..93104eb7 100644 --- a/__tests__/backend/utils/SafeDriveService.test.ts +++ b/tests/backend/utils/SafeDriveService.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from "@jest/globals"; +import { expect, test } from "vitest"; import { SafeDriveService_ } from "../../../src/backend/utils/SafeDriveService"; import { From 52a95ef0e494c22b0e129ae28d421ffd08813c91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:44:10 +0100 Subject: [PATCH 10/23] Migrated SafeCommentsCollection test to vitest --- .../SafeCommentsCollection.test.ts | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) rename {__tests__ => tests}/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts (91%) diff --git a/__tests__/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts similarity index 91% rename from __tests__/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts rename to tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts index beb837e7..954cf5a0 100644 --- a/__tests__/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts @@ -1,5 +1,4 @@ -import { expect, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import { SafeCommentsCollection_ } from "../../../../src/backend/utils/SafeDriveService/SafeCommentsCollection"; import { @@ -48,9 +47,9 @@ test("insert works", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const insert = mocked(global.Drive.Comments).insert.mockReturnValueOnce( - comment, - ); + const insert = vi + .mocked(global.Drive.Comments) + .insert.mockReturnValueOnce(comment); const commentsCollection = new SafeCommentsCollection_(); @@ -80,9 +79,9 @@ test("insert throws an error on an invalid comment", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const insert = mocked(global.Drive.Comments).insert.mockReturnValueOnce( - comment, - ); + const insert = vi + .mocked(global.Drive.Comments) + .insert.mockReturnValueOnce(comment); const commentsCollection = new SafeCommentsCollection_(); @@ -134,9 +133,9 @@ test("list works", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); @@ -188,9 +187,9 @@ test("list works with optional arguments", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); @@ -249,9 +248,9 @@ test("list throws an error on an invalid comment", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); @@ -268,9 +267,9 @@ test("list throws an error on an invalid comment list", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); @@ -313,9 +312,9 @@ test("list throws an error on missing replies", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); @@ -366,9 +365,9 @@ test("list throws an error on an invalid reply", () => { }; global.Drive.Comments = mockedCommentsCollection(); - const list = mocked(global.Drive.Comments).list.mockReturnValueOnce( - commentList, - ); + const list = vi + .mocked(global.Drive.Comments) + .list.mockReturnValueOnce(commentList); const commentsCollection = new SafeCommentsCollection_(); From fe6fc3635b2b4e97bd95eb8c5599f4809e8fed2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:46:22 +0100 Subject: [PATCH 11/23] Migrated SafeDrivesCollection test to vitest --- .../SafeDrivesCollection.test.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) rename {__tests__ => tests}/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts (88%) diff --git a/__tests__/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts similarity index 88% rename from __tests__/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts rename to tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts index d6691c4f..dbe8a5b1 100644 --- a/__tests__/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts @@ -1,5 +1,4 @@ -import { expect, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import { SafeDrivesCollection_ } from "../../../../src/backend/utils/SafeDriveService/SafeDrivesCollection"; import { @@ -43,7 +42,9 @@ test("list works", () => { }; global.Drive.Drives = mockedDrivesCollection(); - const list = mocked(global.Drive.Drives).list.mockReturnValueOnce(driveList); + const list = vi + .mocked(global.Drive.Drives) + .list.mockReturnValueOnce(driveList); const drivesCollection = new SafeDrivesCollection_(); @@ -68,7 +69,9 @@ test("list works with optional parameters", () => { }; global.Drive.Drives = mockedDrivesCollection(); - const list = mocked(global.Drive.Drives).list.mockReturnValueOnce(driveList); + const list = vi + .mocked(global.Drive.Drives) + .list.mockReturnValueOnce(driveList); const drivesCollection = new SafeDrivesCollection_(); @@ -109,7 +112,8 @@ test("list works with selective fields", () => { }; global.Drive.Drives = mockedDrivesCollection(); - const list = mocked(global.Drive.Drives) + const list = vi + .mocked(global.Drive.Drives) .list.mockReturnValueOnce(driveList1) .mockReturnValueOnce(driveList2); @@ -142,7 +146,9 @@ test("list throws an error on an invalid drive", () => { }; global.Drive.Drives = mockedDrivesCollection(); - const list = mocked(global.Drive.Drives).list.mockReturnValueOnce(driveList); + const list = vi + .mocked(global.Drive.Drives) + .list.mockReturnValueOnce(driveList); const drivesCollection = new SafeDrivesCollection_(); @@ -154,7 +160,7 @@ test("list throws an error on an invalid drive", () => { test("list throws an error on an invalid drive list", () => { global.Drive.Drives = mockedDrivesCollection(); - const list = mocked(global.Drive.Drives).list.mockReturnValueOnce({}); + const list = vi.mocked(global.Drive.Drives).list.mockReturnValueOnce({}); const drivesCollection = new SafeDrivesCollection_(); From 9e3c373aab946e195377b643d0b1f4d20d34d5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:47:26 +0100 Subject: [PATCH 12/23] Migrated SafeFilesCollection test to vitest --- .../SafeFilesCollection.test.ts | 47 +++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) rename {__tests__ => tests}/backend/utils/SafeDriveService/SafeFilesCollection.test.ts (89%) diff --git a/__tests__/backend/utils/SafeDriveService/SafeFilesCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts similarity index 89% rename from __tests__/backend/utils/SafeDriveService/SafeFilesCollection.test.ts rename to tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts index 60a4ba4b..37f86626 100644 --- a/__tests__/backend/utils/SafeDriveService/SafeFilesCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts @@ -1,5 +1,4 @@ -import { expect, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import { SafeFilesCollection_ } from "../../../../src/backend/utils/SafeDriveService/SafeFilesCollection"; import { @@ -42,7 +41,7 @@ test("copy works", () => { }; global.Drive.Files = mockedFilesCollection(); - const copy = mocked(global.Drive.Files).copy.mockReturnValueOnce(file); + const copy = vi.mocked(global.Drive.Files).copy.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -68,7 +67,7 @@ test("copy works with optional arguments", () => { }; global.Drive.Files = mockedFilesCollection(); - const copy = mocked(global.Drive.Files).copy.mockReturnValueOnce(file); + const copy = vi.mocked(global.Drive.Files).copy.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -91,7 +90,7 @@ test("copy works with selective fields", () => { }; global.Drive.Files = mockedFilesCollection(); - const copy = mocked(global.Drive.Files).copy.mockReturnValueOnce(file); + const copy = vi.mocked(global.Drive.Files).copy.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -124,7 +123,7 @@ test("copy throws and error on invalid file", () => { }; global.Drive.Files = mockedFilesCollection(); - const copy = mocked(global.Drive.Files).copy.mockReturnValueOnce(file); + const copy = vi.mocked(global.Drive.Files).copy.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -150,7 +149,7 @@ test("get works", () => { }; global.Drive.Files = mockedFilesCollection(); - const get = mocked(global.Drive.Files).get.mockReturnValueOnce(file); + const get = vi.mocked(global.Drive.Files).get.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -175,7 +174,7 @@ test("get works with optional arguments", () => { }; global.Drive.Files = mockedFilesCollection(); - const get = mocked(global.Drive.Files).get.mockReturnValueOnce(file); + const get = vi.mocked(global.Drive.Files).get.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -193,7 +192,7 @@ test("get works with selective fields", () => { }; global.Drive.Files = mockedFilesCollection(); - const get = mocked(global.Drive.Files).get.mockReturnValueOnce(file); + const get = vi.mocked(global.Drive.Files).get.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -217,7 +216,7 @@ test("get throws an error on invalid file", () => { }; global.Drive.Files = mockedFilesCollection(); - const get = mocked(global.Drive.Files).get.mockReturnValueOnce(file); + const get = vi.mocked(global.Drive.Files).get.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -242,7 +241,7 @@ test("insert works", () => { }; global.Drive.Files = mockedFilesCollection(); - const insert = mocked(global.Drive.Files).insert.mockReturnValueOnce(file); + const insert = vi.mocked(global.Drive.Files).insert.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -268,7 +267,7 @@ test("insert works with optional arguments", () => { }; global.Drive.Files = mockedFilesCollection(); - const insert = mocked(global.Drive.Files).insert.mockReturnValueOnce(file); + const insert = vi.mocked(global.Drive.Files).insert.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -291,7 +290,7 @@ test("insert works with selective fields", () => { }; global.Drive.Files = mockedFilesCollection(); - const insert = mocked(global.Drive.Files).insert.mockReturnValueOnce(file); + const insert = vi.mocked(global.Drive.Files).insert.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -322,7 +321,7 @@ test("insert throws an error on invalid file", () => { }; global.Drive.Files = mockedFilesCollection(); - const insert = mocked(global.Drive.Files).insert.mockReturnValueOnce(file); + const insert = vi.mocked(global.Drive.Files).insert.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -363,7 +362,7 @@ test("list works", () => { }; global.Drive.Files = mockedFilesCollection(); - const list = mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); + const list = vi.mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); const filesCollection = new SafeFilesCollection_(); @@ -402,7 +401,7 @@ test("list works with optional arguments", () => { }; global.Drive.Files = mockedFilesCollection(); - const list = mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); + const list = vi.mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); const filesCollection = new SafeFilesCollection_(); @@ -439,7 +438,7 @@ test("list works with selective fields", () => { }; global.Drive.Files = mockedFilesCollection(); - const list = mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); + const list = vi.mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); const filesCollection = new SafeFilesCollection_(); @@ -480,7 +479,7 @@ test("list throws an error on invalid file", () => { }; global.Drive.Files = mockedFilesCollection(); - const list = mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); + const list = vi.mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); const filesCollection = new SafeFilesCollection_(); @@ -494,7 +493,7 @@ test("list throws an error on invalid file list", () => { const fileList = {}; global.Drive.Files = mockedFilesCollection(); - const list = mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); + const list = vi.mocked(global.Drive.Files).list.mockReturnValueOnce(fileList); const filesCollection = new SafeFilesCollection_(); @@ -506,7 +505,7 @@ test("list throws an error on invalid file list", () => { test("remove works", () => { global.Drive.Files = mockedFilesCollection(); - const remove = mocked(global.Drive.Files).remove.mockImplementationOnce( + const remove = vi.mocked(global.Drive.Files).remove.mockImplementationOnce( // eslint-disable-next-line @typescript-eslint/no-empty-function -- Implementation needed so the function is bound to local this () => {}, ); @@ -533,7 +532,7 @@ test("update works", () => { }; global.Drive.Files = mockedFilesCollection(); - const update = mocked(global.Drive.Files).update.mockReturnValueOnce(file); + const update = vi.mocked(global.Drive.Files).update.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -560,7 +559,7 @@ test("update works with optional arguments", () => { }; global.Drive.Files = mockedFilesCollection(); - const update = mocked(global.Drive.Files).update.mockReturnValueOnce(file); + const update = vi.mocked(global.Drive.Files).update.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -589,7 +588,7 @@ test("update works with selective fields", () => { }; global.Drive.Files = mockedFilesCollection(); - const update = mocked(global.Drive.Files).update.mockReturnValueOnce(file); + const update = vi.mocked(global.Drive.Files).update.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); @@ -614,7 +613,7 @@ test("update throws an error on invalid file", () => { }; global.Drive.Files = mockedFilesCollection(); - const update = mocked(global.Drive.Files).update.mockReturnValueOnce(file); + const update = vi.mocked(global.Drive.Files).update.mockReturnValueOnce(file); const filesCollection = new SafeFilesCollection_(); From 5ba8bc1e053e5ce2715a6ee6c66107038c9638e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:52:54 +0100 Subject: [PATCH 13/23] Migrated move test to vitest --- __tests__/backend/move.test.ts | 557 ----------------------------- tests/backend/move.test.ts | 544 ++++++++++++++++++++++++++++ tests/test-utils/MoveState-stub.ts | 18 + 3 files changed, 562 insertions(+), 557 deletions(-) delete mode 100644 __tests__/backend/move.test.ts create mode 100644 tests/backend/move.test.ts create mode 100644 tests/test-utils/MoveState-stub.ts diff --git a/__tests__/backend/move.test.ts b/__tests__/backend/move.test.ts deleted file mode 100644 index 630a1029..00000000 --- a/__tests__/backend/move.test.ts +++ /dev/null @@ -1,557 +0,0 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; - -import { move } from "../../src/backend/move"; -import * as folderManagement from "../../src/backend/move/folderManagement"; -import * as moveFolder from "../../src/backend/move/moveFolder"; -import { MoveState_ } from "../../src/backend/utils/MoveState"; -import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedMoveState } from "../test-utils/MoveState-stub"; - -jest.mock("../../src/backend/move/folderManagement"); -jest.mock("../../src/backend/move/moveFolder"); -jest.mock<{ SafeDriveService_: jest.Mock }>( - "../../src/backend/utils/SafeDriveService", - () => ({ - SafeDriveService_: jest.fn(), - }), -); -jest.mock<{ MoveState_: jest.Mock }>( - "../../src/backend/utils/MoveState", - () => ({ - MoveState_: jest.fn(), - }), -); - -test("move works correctly", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); -}); - -test("move works correctly with subfolders", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValueOnce({ - destinationID: "SUB1_DEST_ID", - path: ["SUBFOLDER1"], - sourceID: "SUB1_SRC_ID", - }) - .mockReturnValueOnce({ - destinationID: "SUB2_DEST_ID", - path: ["SUBFOLDER2"], - sourceID: "SUB2_SRC_ID", - }) - .mockReturnValueOnce({ - destinationID: "SUB3_DEST_ID", - path: ["PATH", "TO", "SOME", "DEEP", "SUBFOLDER3"], - sourceID: "SUB3_SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(5); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(4); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[1][1].sourceID).toBe( - "SUB1_SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[1][1].destinationID).toBe( - "SUB1_DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[1][1].path).toStrictEqual([ - "SUBFOLDER1", - ]); - expect(mocked(moveFolder).moveFolder_.mock.calls[1][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[1][3]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[2][1].sourceID).toBe( - "SUB2_SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[2][1].destinationID).toBe( - "SUB2_DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[2][1].path).toStrictEqual([ - "SUBFOLDER2", - ]); - expect(mocked(moveFolder).moveFolder_.mock.calls[2][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[2][3]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[3][1].sourceID).toBe( - "SUB3_SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[3][1].destinationID).toBe( - "SUB3_DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[3][1].path).toStrictEqual([ - "PATH", - "TO", - "SOME", - "DEEP", - "SUBFOLDER3", - ]); - expect(mocked(moveFolder).moveFolder_.mock.calls[3][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[3][3]).toBe(false); -}); - -test("move passes copyComments correctly", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", true, false, false)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(true); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(true); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); -}); - -test("move passes mergeFolders correctly", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, true, false)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(true); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(true); -}); - -test("move detects and reports source matching destination", () => { - expect(move("SRC_ID", "SRC_ID", false, false, false)).toStrictEqual({ - status: "error", - type: "sourceEqualsDestination", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(0); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(0); - expect(mocked(MoveState_).mock.calls).toHaveLength(0); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); -}); - -test("move fails gracefully on error when checking folder emptiness", () => { - mocked(folderManagement).isFolderEmpty_.mockImplementationOnce(() => { - throw new Error(); - }); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - status: "error", - type: "DriveAPIError", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(0); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); -}); - -test("move fails gracefully on non-empty destination directory", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); - const moveStateMock = mockedMoveState(); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - status: "error", - type: "notEmpty", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(0); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); -}); - -test("move doesn't care about non-empty destination directory when resuming from an existing move", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(false).mockReturnValueOnce(false); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); -}); - -test("move works correctly with non-empty override", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move("SRC_ID", "DEST_ID", false, false, true)).toStrictEqual({ - response: { - errors: [], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); -}); - -test("move fails gracefully on invalid parameter types", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); - const moveStateMock = mockedMoveState(); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - - expect(move(42, "DEST_ID", false, false, false)).toStrictEqual({ - status: "error", - type: "invalidParameter", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(0); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(0); - expect(mocked(MoveState_).mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(0); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(0); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); -}); - -test("move fails gracefully on error while moving", () => { - mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); - const error = { - error: "ERROR_MESSAGE", - file: ["PATH", "TO", "FILE"], - }; - const moveStateMock = mockedMoveState(); - moveStateMock.getNextPath - .mockReturnValueOnce({ - destinationID: "DEST_ID", - path: [], - sourceID: "SRC_ID", - }) - .mockReturnValue(null); - moveStateMock.getErrors.mockReturnValueOnce([error]); - moveStateMock.isNull.mockReturnValueOnce(true); - mocked(MoveState_).mockReturnValue(moveStateMock); - mocked(moveFolder).moveFolder_.mockImplementationOnce((state: MoveState_) => { - state.logError(error.file, error.error); - }); - const consoleError = jest - .spyOn(console, "error") - // eslint-disable-next-line @typescript-eslint/no-empty-function -- The function is needed just to detect its calls - .mockImplementationOnce(() => {}); - - expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ - response: { - errors: [error], - }, - status: "success", - }); - - expect(mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); - expect(mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( - "SRC_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( - "DEST_ID", - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( - [], - ); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); - expect(mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); - expect(mocked(SafeDriveService_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls).toHaveLength(1); - expect(mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(MoveState_).mock.calls[0][2]).toBe(false); - expect(mocked(MoveState_).mock.calls[0][3]).toBe(false); - expect(mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); - expect(mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); - expect(mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); - expect(mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); - expect(mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); - expect(consoleError.mock.calls).toHaveLength(1); - expect(consoleError.mock.calls[0][0]).toStrictEqual([error]); -}); diff --git a/tests/backend/move.test.ts b/tests/backend/move.test.ts new file mode 100644 index 00000000..597556e5 --- /dev/null +++ b/tests/backend/move.test.ts @@ -0,0 +1,544 @@ +import { expect, test, vi } from "vitest"; + +import { move } from "../../src/backend/move"; +import * as folderManagement from "../../src/backend/move/folderManagement"; +import * as moveFolder from "../../src/backend/move/moveFolder"; +import { MoveState_ } from "../../src/backend/utils/MoveState"; +import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; +import { mockedMoveState } from "../test-utils/MoveState-stub"; + +vi.mock("../../src/backend/move/folderManagement"); +vi.mock("../../src/backend/move/moveFolder"); +vi.mock("../../src/backend/utils/SafeDriveService"); +vi.mock("../../src/backend/utils/MoveState"); + +test("move works correctly", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); +}); + +test("move works correctly with subfolders", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValueOnce({ + destinationID: "SUB1_DEST_ID", + path: ["SUBFOLDER1"], + sourceID: "SUB1_SRC_ID", + }) + .mockReturnValueOnce({ + destinationID: "SUB2_DEST_ID", + path: ["SUBFOLDER2"], + sourceID: "SUB2_SRC_ID", + }) + .mockReturnValueOnce({ + destinationID: "SUB3_DEST_ID", + path: ["PATH", "TO", "SOME", "DEEP", "SUBFOLDER3"], + sourceID: "SUB3_SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(5); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(4); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[1][1].sourceID).toBe( + "SUB1_SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[1][1].destinationID).toBe( + "SUB1_DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[1][1].path).toStrictEqual( + ["SUBFOLDER1"], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[1][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[1][3]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[2][1].sourceID).toBe( + "SUB2_SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[2][1].destinationID).toBe( + "SUB2_DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[2][1].path).toStrictEqual( + ["SUBFOLDER2"], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[2][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[2][3]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[3][1].sourceID).toBe( + "SUB3_SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[3][1].destinationID).toBe( + "SUB3_DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[3][1].path).toStrictEqual( + ["PATH", "TO", "SOME", "DEEP", "SUBFOLDER3"], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[3][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[3][3]).toBe(false); +}); + +test("move passes copyComments correctly", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", true, false, false)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(true); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(true); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); +}); + +test("move passes mergeFolders correctly", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, true, false)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(true); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(true); +}); + +test("move detects and reports source matching destination", () => { + expect(move("SRC_ID", "SRC_ID", false, false, false)).toStrictEqual({ + status: "error", + type: "sourceEqualsDestination", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(0); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(0); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(0); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); +}); + +test("move fails gracefully on error when checking folder emptiness", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockImplementationOnce(() => { + throw new Error(); + }); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + status: "error", + type: "DriveAPIError", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(0); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); +}); + +test("move fails gracefully on non-empty destination directory", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); + const moveStateMock = mockedMoveState(); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + status: "error", + type: "notEmpty", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(0); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); +}); + +test("move doesn't care about non-empty destination directory when resuming from an existing move", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(false).mockReturnValueOnce(false); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); +}); + +test("move works correctly with non-empty override", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move("SRC_ID", "DEST_ID", false, false, true)).toStrictEqual({ + response: { + errors: [], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); +}); + +test("move fails gracefully on invalid parameter types", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(false); + const moveStateMock = mockedMoveState(); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + + expect(move(42, "DEST_ID", false, false, false)).toStrictEqual({ + status: "error", + type: "invalidParameter", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(0); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(0); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(0); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(0); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(0); +}); + +test("move fails gracefully on error while moving", () => { + vi.mocked(folderManagement).isFolderEmpty_.mockReturnValueOnce(true); + const error = { + error: "ERROR_MESSAGE", + file: ["PATH", "TO", "FILE"], + }; + const moveStateMock = mockedMoveState(); + moveStateMock.getNextPath + .mockReturnValueOnce({ + destinationID: "DEST_ID", + path: [], + sourceID: "SRC_ID", + }) + .mockReturnValue(null); + moveStateMock.getErrors.mockReturnValueOnce([error]); + moveStateMock.isNull.mockReturnValueOnce(true); + vi.mocked(MoveState_).mockReturnValue(moveStateMock); + vi.mocked(moveFolder).moveFolder_.mockImplementationOnce( + (state: MoveState_) => { + state.logError(error.file, error.error); + }, + ); + const consoleError = vi + .spyOn(console, "error") + // eslint-disable-next-line @typescript-eslint/no-empty-function -- The function is needed just to detect its calls + .mockImplementationOnce(() => {}); + + expect(move("SRC_ID", "DEST_ID", false, false, false)).toStrictEqual({ + response: { + errors: [error], + }, + status: "success", + }); + + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls).toHaveLength(1); + expect(vi.mocked(folderManagement).isFolderEmpty_.mock.calls[0][0]).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls).toHaveLength(1); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].sourceID).toBe( + "SRC_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].destinationID).toBe( + "DEST_ID", + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][1].path).toStrictEqual( + [], + ); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][2]).toBe(false); + expect(vi.mocked(moveFolder).moveFolder_.mock.calls[0][3]).toBe(false); + expect(vi.mocked(SafeDriveService_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls).toHaveLength(1); + expect(vi.mocked(MoveState_).mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(MoveState_).mock.calls[0][2]).toBe(false); + expect(vi.mocked(MoveState_).mock.calls[0][3]).toBe(false); + expect(vi.mocked(moveStateMock).loadState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).isNull.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][0]).toBe("SRC_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][1]).toBe("DEST_ID"); + expect(vi.mocked(moveStateMock).addPath.mock.calls[0][2]).toStrictEqual([]); + expect(vi.mocked(moveStateMock).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).getNextPath.mock.calls).toHaveLength(2); + expect(vi.mocked(moveStateMock).getErrors.mock.calls).toHaveLength(1); + expect(vi.mocked(moveStateMock).destroyState.mock.calls).toHaveLength(1); + expect(consoleError.mock.calls).toHaveLength(1); + expect(consoleError.mock.calls[0][0]).toStrictEqual([error]); +}); diff --git a/tests/test-utils/MoveState-stub.ts b/tests/test-utils/MoveState-stub.ts new file mode 100644 index 00000000..a8fe7281 --- /dev/null +++ b/tests/test-utils/MoveState-stub.ts @@ -0,0 +1,18 @@ +import { type MockedObject, vi } from "vitest"; + +import type { MoveState_ } from "../../src/backend/utils/MoveState"; + +export function mockedMoveState(): MockedObject { + return { + addPath: vi.fn(), + destroyState: vi.fn(), + getErrors: vi.fn(), + getNextPath: vi.fn(), + isNull: vi.fn(), + loadState: vi.fn(), + logError: vi.fn(), + removePath: vi.fn(), + saveState: vi.fn(), + tryOrLog: vi.fn(), + } as unknown as MockedObject; +} From 3bfb2e20b5092d7bc2f8d350799f9490c8592a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 15:59:17 +0100 Subject: [PATCH 14/23] Migrated copyFileComments test to vitest --- .../backend/move/copyFileComments.test.ts | 165 --------------- tests/backend/move/copyFileComments.test.ts | 195 ++++++++++++++++++ 2 files changed, 195 insertions(+), 165 deletions(-) delete mode 100644 __tests__/backend/move/copyFileComments.test.ts create mode 100644 tests/backend/move/copyFileComments.test.ts diff --git a/__tests__/backend/move/copyFileComments.test.ts b/__tests__/backend/move/copyFileComments.test.ts deleted file mode 100644 index d67395ba..00000000 --- a/__tests__/backend/move/copyFileComments.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { expect, test } from "@jest/globals"; - -import { copyFileComments_ } from "../../../src/backend/move/copyFileComments"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; - -test("copyFileComments works correctly", () => { - interface ListCommentsOptions { - fields?: string; - maxResults?: number; - pageToken?: string; - } - - const rawResponse = { - items: [ - { - author: { displayName: "COM1_AUTH", isAuthenticatedUser: false }, - commentId: "SRC_COM1_ID", - content: "COM1_CONTENT", - replies: [], - }, - { - author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, - commentId: "SRC_COM2_ID", - content: "COM2_CONTENT", - replies: [], - }, - ], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Comments.insert - .mockReturnValueOnce({ - author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, - commentId: "DEST_COM1_ID", - content: "*COM1_AUTH:*\nCOM1_CONTENT", - replies: [], - }) - .mockReturnValueOnce({ - author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, - commentId: "DEST_COM2_ID", - content: "COM2_CONTENT", - replies: [], - }); - driveServiceMock.Comments.list.mockReturnValueOnce(rawResponse); - - copyFileComments_("SRC_FILE_ID", "DEST_FILE_ID", driveServiceMock); - - expect(driveServiceMock.Comments.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Comments.list.mock.calls[0][0]).toBe("SRC_FILE_ID"); - expect(driveServiceMock.Comments.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions) - .pageToken, - ).toBeUndefined(); - expect( - (driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions) - .fields, - ).toBeDefined(); - expect( - ( - driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions - ).fields - ?.split(",") - .map((s) => s.trim()), - ).toContain("nextPageToken"); - expect(driveServiceMock.Comments.insert.mock.calls).toHaveLength(2); - expect(driveServiceMock.Comments.insert.mock.calls[0][0].content).toBe( - "*COM1_AUTH:*\nCOM1_CONTENT", - ); - expect( - driveServiceMock.Comments.insert.mock.calls[0][0].replies, - ).toStrictEqual([]); - expect(driveServiceMock.Comments.insert.mock.calls[0][1]).toBe( - "DEST_FILE_ID", - ); - expect(driveServiceMock.Comments.insert.mock.calls[1][0].content).toBe( - "COM2_CONTENT", - ); - expect( - driveServiceMock.Comments.insert.mock.calls[1][0].replies, - ).toStrictEqual([]); - expect(driveServiceMock.Comments.insert.mock.calls[1][1]).toBe( - "DEST_FILE_ID", - ); -}); - -test("copyFileComments works correctly with replies", () => { - interface ListCommentsOptions { - fields?: string; - maxResults?: number; - pageToken?: string; - } - - const rawResponse = { - items: [ - { - author: { displayName: "COM_AUTH", isAuthenticatedUser: true }, - commentId: "SRC_COM_ID", - content: "COM_CONTENT", - replies: [ - { - author: { displayName: "REPLY1_AUTH", isAuthenticatedUser: false }, - content: "REPLY1_CONTENT", - }, - { - author: { displayName: "REPLY2_AUTH", isAuthenticatedUser: true }, - content: "REPLY2_CONTENT", - }, - ], - }, - ], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Comments.insert.mockReturnValueOnce({ - author: { displayName: "COM_AUTH", isAuthenticatedUser: true }, - commentId: "DEST_COM_ID", - content: "COM_CONTENT", - replies: [], - }); - driveServiceMock.Comments.list.mockReturnValueOnce(rawResponse); - driveServiceMock.Replies.insert.mockReturnValueOnce({}); - - copyFileComments_("SRC_FILE_ID", "DEST_FILE_ID", driveServiceMock); - - expect(driveServiceMock.Comments.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Comments.list.mock.calls[0][0]).toBe("SRC_FILE_ID"); - expect(driveServiceMock.Comments.list.mock.calls[0][1]).toBeDefined(); - expect( - (driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions) - .pageToken, - ).toBeUndefined(); - expect( - (driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions) - .fields, - ).toBeDefined(); - expect( - ( - driveServiceMock.Comments.list.mock.calls[0][1] as ListCommentsOptions - ).fields - ?.split(",") - .map((s) => s.trim()), - ).toContain("nextPageToken"); - expect(driveServiceMock.Comments.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Comments.insert.mock.calls[0][0].content).toBe( - "COM_CONTENT", - ); - expect( - driveServiceMock.Comments.insert.mock.calls[0][0].replies, - ).toStrictEqual([]); - expect(driveServiceMock.Comments.insert.mock.calls[0][1]).toBe( - "DEST_FILE_ID", - ); - expect(driveServiceMock.Replies.insert.mock.calls).toHaveLength(2); - expect(driveServiceMock.Replies.insert.mock.calls[0][0].content).toBe( - "*REPLY1_AUTH:*\nREPLY1_CONTENT", - ); - expect(driveServiceMock.Replies.insert.mock.calls[0][1]).toBe("DEST_FILE_ID"); - expect(driveServiceMock.Replies.insert.mock.calls[0][2]).toBe("DEST_COM_ID"); - expect(driveServiceMock.Replies.insert.mock.calls[1][0].content).toBe( - "REPLY2_CONTENT", - ); - expect(driveServiceMock.Replies.insert.mock.calls[1][1]).toBe("DEST_FILE_ID"); - expect(driveServiceMock.Replies.insert.mock.calls[1][2]).toBe("DEST_COM_ID"); -}); diff --git a/tests/backend/move/copyFileComments.test.ts b/tests/backend/move/copyFileComments.test.ts new file mode 100644 index 00000000..adcc8994 --- /dev/null +++ b/tests/backend/move/copyFileComments.test.ts @@ -0,0 +1,195 @@ +import { expect, test, vi } from "vitest"; + +import { copyFileComments_ } from "../../../src/backend/move/copyFileComments"; +import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; + +test("copyFileComments works correctly", () => { + interface ListCommentsOptions { + fields?: string; + maxResults?: number; + pageToken?: string; + } + + const rawResponse = { + items: [ + { + author: { displayName: "COM1_AUTH", isAuthenticatedUser: false }, + commentId: "SRC_COM1_ID", + content: "COM1_CONTENT", + replies: [], + }, + { + author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, + commentId: "SRC_COM2_ID", + content: "COM2_CONTENT", + replies: [], + }, + ], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Comments.insert) + .mockReturnValueOnce({ + author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, + commentId: "DEST_COM1_ID", + content: "*COM1_AUTH:*\nCOM1_CONTENT", + replies: [], + }) + .mockReturnValueOnce({ + author: { displayName: "COM2_AUTH", isAuthenticatedUser: true }, + commentId: "DEST_COM2_ID", + content: "COM2_CONTENT", + replies: [], + }); + vi.mocked(driveServiceMock.Comments.list).mockReturnValueOnce(rawResponse); + + copyFileComments_("SRC_FILE_ID", "DEST_FILE_ID", driveServiceMock); + + expect(vi.mocked(driveServiceMock.Comments.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Comments.list).mock.calls[0][0]).toBe( + "SRC_FILE_ID", + ); + expect( + vi.mocked(driveServiceMock.Comments.list).mock.calls[0][1], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).pageToken, + ).toBeUndefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).fields, + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).fields + ?.split(",") + .map((s) => s.trim()), + ).toContain("nextPageToken"); + expect(vi.mocked(driveServiceMock.Comments.insert).mock.calls).toHaveLength( + 2, + ); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][0].content, + ).toBe("*COM1_AUTH:*\nCOM1_CONTENT"); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][0].replies, + ).toStrictEqual([]); + expect(vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][1]).toBe( + "DEST_FILE_ID", + ); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[1][0].content, + ).toBe("COM2_CONTENT"); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[1][0].replies, + ).toStrictEqual([]); + expect(vi.mocked(driveServiceMock.Comments.insert).mock.calls[1][1]).toBe( + "DEST_FILE_ID", + ); +}); + +test("copyFileComments works correctly with replies", () => { + interface ListCommentsOptions { + fields?: string; + maxResults?: number; + pageToken?: string; + } + + const rawResponse = { + items: [ + { + author: { displayName: "COM_AUTH", isAuthenticatedUser: true }, + commentId: "SRC_COM_ID", + content: "COM_CONTENT", + replies: [ + { + author: { displayName: "REPLY1_AUTH", isAuthenticatedUser: false }, + content: "REPLY1_CONTENT", + }, + { + author: { displayName: "REPLY2_AUTH", isAuthenticatedUser: true }, + content: "REPLY2_CONTENT", + }, + ], + }, + ], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Comments.insert).mockReturnValueOnce({ + author: { displayName: "COM_AUTH", isAuthenticatedUser: true }, + commentId: "DEST_COM_ID", + content: "COM_CONTENT", + replies: [], + }); + vi.mocked(driveServiceMock.Comments.list).mockReturnValueOnce(rawResponse); + vi.mocked(driveServiceMock.Replies.insert).mockReturnValueOnce({}); + + copyFileComments_("SRC_FILE_ID", "DEST_FILE_ID", driveServiceMock); + + expect(vi.mocked(driveServiceMock.Comments.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Comments.list).mock.calls[0][0]).toBe( + "SRC_FILE_ID", + ); + expect( + vi.mocked(driveServiceMock.Comments.list).mock.calls[0][1], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).pageToken, + ).toBeUndefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).fields, + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Comments.list).mock + .calls[0][1] as ListCommentsOptions + ).fields + ?.split(",") + .map((s) => s.trim()), + ).toContain("nextPageToken"); + expect(vi.mocked(driveServiceMock.Comments.insert).mock.calls).toHaveLength( + 1, + ); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][0].content, + ).toBe("COM_CONTENT"); + expect( + vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][0].replies, + ).toStrictEqual([]); + expect(vi.mocked(driveServiceMock.Comments.insert).mock.calls[0][1]).toBe( + "DEST_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Replies.insert).mock.calls).toHaveLength(2); + expect( + vi.mocked(driveServiceMock.Replies.insert).mock.calls[0][0].content, + ).toBe("*REPLY1_AUTH:*\nREPLY1_CONTENT"); + expect(vi.mocked(driveServiceMock.Replies.insert).mock.calls[0][1]).toBe( + "DEST_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Replies.insert).mock.calls[0][2]).toBe( + "DEST_COM_ID", + ); + expect( + vi.mocked(driveServiceMock.Replies.insert).mock.calls[1][0].content, + ).toBe("REPLY2_CONTENT"); + expect(vi.mocked(driveServiceMock.Replies.insert).mock.calls[1][1]).toBe( + "DEST_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Replies.insert).mock.calls[1][2]).toBe( + "DEST_COM_ID", + ); +}); From 479f2a20ff406c699251a3eb3f807c8e845d3c59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:09:54 +0100 Subject: [PATCH 15/23] Migrated folderManagement test to vitest --- .../backend/move/folderManagement.test.ts | 321 -------------- tests/backend/move/folderManagement.test.ts | 402 ++++++++++++++++++ 2 files changed, 402 insertions(+), 321 deletions(-) delete mode 100644 __tests__/backend/move/folderManagement.test.ts create mode 100644 tests/backend/move/folderManagement.test.ts diff --git a/__tests__/backend/move/folderManagement.test.ts b/__tests__/backend/move/folderManagement.test.ts deleted file mode 100644 index f3eea5de..00000000 --- a/__tests__/backend/move/folderManagement.test.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { expect, test } from "@jest/globals"; - -import { - deleteFolderIfEmpty_, - isFolderEmpty_, - listFilesInFolder_, - listFoldersInFolder_, -} from "../../../src/backend/move/folderManagement"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; - -test("listFilesInFolder works correctly", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const items = [ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE1_ID", - title: "FILE1_TITLE", - }, - { - capabilities: { canMoveItemOutOfDrive: false }, - id: "FILE2_ID", - title: "FILE2_TITLE", - }, - ]; - const rawResponse = { - items, - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - - expect(listFilesInFolder_("FOLDER_ID", driveServiceMock)).toStrictEqual( - items, - ); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType != "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .pageToken, - ).toBeUndefined(); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toStrictEqual({ - capabilities: { canMoveItemOutOfDrive: true }, - id: true, - title: true, - }); -}); - -test("listFoldersInFolder works correctly", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const items = [ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE1_ID", - title: "FILE1_TITLE", - }, - { - capabilities: { canMoveItemOutOfDrive: false }, - id: "FILE2_ID", - title: "FILE2_TITLE", - }, - ]; - const rawResponse = { - items, - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - - expect(listFoldersInFolder_("FOLDER_ID", driveServiceMock)).toStrictEqual( - items, - ); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('mimeType = "application/vnd.google-apps.folder"'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .pageToken, - ).toBeUndefined(); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toStrictEqual({ - capabilities: { canMoveItemOutOfDrive: true }, - id: true, - title: true, - }); -}); - -test("isFolderEmpty works correctly with an empty folder", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - q?: string; - supportsAllDrives?: boolean; - } - - const rawResponse = { - items: [], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - - expect(isFolderEmpty_("ID_FOLDER", driveServiceMock)).toBe(true); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("ID_FOLDER"); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); -}); - -test("isFolderEmpty works correctly with a non-empty folder", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - q?: string; - supportsAllDrives?: boolean; - } - - const rawResponse = { - items: [{ id: "ID_FILE" }], - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(rawResponse); - - expect(isFolderEmpty_("ID_FOLDER", driveServiceMock)).toBe(false); - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain("ID_FOLDER"); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); -}); - -test.each(["owner", "organizer"] as Array< - "fileOrganizer" | "organizer" | "owner" | "reader" | "writer" ->)( - "deleteFolderIfEmpty works correctly", - (role: "fileOrganizer" | "organizer" | "owner" | "reader" | "writer") => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const getResponse = { - userPermission: { role }, - }; - const listResponse = { - items: [], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.get.mockReturnValueOnce(getResponse); - driveServiceMock.Files.list.mockReturnValueOnce(listResponse); - - deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.get.mock.calls[0][0]).toBe("FOLDER_ID"); - expect(driveServiceMock.Files.get.mock.calls[0][1]).toBeDefined(); - expect(driveServiceMock.Files.get.mock.calls[0][1]).toStrictEqual({ - userPermission: { role: true }, - }); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.remove.mock.calls[0][0]).toBe("FOLDER_ID"); - }, -); - -test("deleteFolderIfEmpty doesn't delete a non-empty folder", () => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const listResponse = { - items: [{ userPermission: { role: "reader" } }], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.list.mockReturnValueOnce(listResponse); - - deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(0); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(0); -}); - -test.each(["fileOrganizer", "reader", "writer"] as Array< - "fileOrganizer" | "organizer" | "owner" | "reader" | "writer" ->)( - "deleteFolderIfEmpty doesn't try to delete a folder without permissions", - (role: "fileOrganizer" | "organizer" | "owner" | "reader" | "writer") => { - interface ListFilesOptions { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - } - - const getResponse = { - userPermission: { role }, - }; - const listResponse = { - items: [], - nextPageToken: undefined, - }; - const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.get.mockReturnValueOnce(getResponse); - driveServiceMock.Files.list.mockReturnValueOnce(listResponse); - - deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); - - expect(driveServiceMock.Files.list.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.list.mock.calls[0][0]).toBeDefined(); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions).q, - ).toContain('"FOLDER_ID" in parents'); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .includeItemsFromAllDrives, - ).toBe(true); - expect( - (driveServiceMock.Files.list.mock.calls[0][1] as ListFilesOptions) - .supportsAllDrives, - ).toBe(true); - expect(driveServiceMock.Files.get.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.get.mock.calls[0][0]).toBe("FOLDER_ID"); - expect(driveServiceMock.Files.get.mock.calls[0][1]).toBeDefined(); - expect(driveServiceMock.Files.get.mock.calls[0][1]).toStrictEqual({ - userPermission: { role: true }, - }); - expect(driveServiceMock.Files.remove.mock.calls).toHaveLength(0); - }, -); diff --git a/tests/backend/move/folderManagement.test.ts b/tests/backend/move/folderManagement.test.ts new file mode 100644 index 00000000..c169dd40 --- /dev/null +++ b/tests/backend/move/folderManagement.test.ts @@ -0,0 +1,402 @@ +import { expect, test, vi } from "vitest"; + +import { + deleteFolderIfEmpty_, + isFolderEmpty_, + listFilesInFolder_, + listFoldersInFolder_, +} from "../../../src/backend/move/folderManagement"; +import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; + +test("listFilesInFolder works correctly", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const items = [ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE1_ID", + title: "FILE1_TITLE", + }, + { + capabilities: { canMoveItemOutOfDrive: false }, + id: "FILE2_ID", + title: "FILE2_TITLE", + }, + ]; + const rawResponse = { + items, + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + + expect(listFilesInFolder_("FOLDER_ID", driveServiceMock)).toStrictEqual( + items, + ); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType != "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).pageToken, + ).toBeUndefined(); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toStrictEqual( + { + capabilities: { canMoveItemOutOfDrive: true }, + id: true, + title: true, + }, + ); +}); + +test("listFoldersInFolder works correctly", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const items = [ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE1_ID", + title: "FILE1_TITLE", + }, + { + capabilities: { canMoveItemOutOfDrive: false }, + id: "FILE2_ID", + title: "FILE2_TITLE", + }, + ]; + const rawResponse = { + items, + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + + expect(listFoldersInFolder_("FOLDER_ID", driveServiceMock)).toStrictEqual( + items, + ); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('mimeType = "application/vnd.google-apps.folder"'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).pageToken, + ).toBeUndefined(); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toStrictEqual( + { + capabilities: { canMoveItemOutOfDrive: true }, + id: true, + title: true, + }, + ); +}); + +test("isFolderEmpty works correctly with an empty folder", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + q?: string; + supportsAllDrives?: boolean; + } + + const rawResponse = { + items: [], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + + expect(isFolderEmpty_("ID_FOLDER", driveServiceMock)).toBe(true); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("ID_FOLDER"); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); +}); + +test("isFolderEmpty works correctly with a non-empty folder", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + q?: string; + supportsAllDrives?: boolean; + } + + const rawResponse = { + items: [{ id: "ID_FILE" }], + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(rawResponse); + + expect(isFolderEmpty_("ID_FOLDER", driveServiceMock)).toBe(false); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain("ID_FOLDER"); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); +}); + +test.each(["owner", "organizer"] as Array< + "fileOrganizer" | "organizer" | "owner" | "reader" | "writer" +>)( + "deleteFolderIfEmpty works correctly", + (role: "fileOrganizer" | "organizer" | "owner" | "reader" | "writer") => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const getResponse = { + userPermission: { role }, + }; + const listResponse = { + items: [], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.get).mockReturnValueOnce(getResponse); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(listResponse); + + deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.list).mock.calls[0][0], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls[0][0]).toBe( + "FOLDER_ID", + ); + expect( + vi.mocked(driveServiceMock.Files.get).mock.calls[0][1], + ).toBeDefined(); + expect( + vi.mocked(driveServiceMock.Files.get).mock.calls[0][1], + ).toStrictEqual({ + userPermission: { role: true }, + }); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls[0][0]).toBe( + "FOLDER_ID", + ); + }, +); + +test("deleteFolderIfEmpty doesn't delete a non-empty folder", () => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const listResponse = { + items: [{ userPermission: { role: "reader" } }], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(listResponse); + + deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.list).mock.calls[0][0]).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(0); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(0); +}); + +test.each(["fileOrganizer", "reader", "writer"] as Array< + "fileOrganizer" | "organizer" | "owner" | "reader" | "writer" +>)( + "deleteFolderIfEmpty doesn't try to delete a folder without permissions", + (role: "fileOrganizer" | "organizer" | "owner" | "reader" | "writer") => { + interface ListFilesOptions { + includeItemsFromAllDrives?: boolean; + maxResults?: number; + pageToken?: string; + q?: string; + supportsAllDrives?: boolean; + } + + const getResponse = { + userPermission: { role }, + }; + const listResponse = { + items: [], + nextPageToken: undefined, + }; + const driveServiceMock = mockedSafeDriveService(); + vi.mocked(driveServiceMock.Files.get).mockReturnValueOnce(getResponse); + vi.mocked(driveServiceMock.Files.list).mockReturnValueOnce(listResponse); + + deleteFolderIfEmpty_("FOLDER_ID", driveServiceMock); + + expect(vi.mocked(driveServiceMock.Files.list).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.list).mock.calls[0][0], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).q, + ).toContain('"FOLDER_ID" in parents'); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).includeItemsFromAllDrives, + ).toBe(true); + expect( + ( + vi.mocked(driveServiceMock.Files.list).mock + .calls[0][1] as ListFilesOptions + ).supportsAllDrives, + ).toBe(true); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.get).mock.calls[0][0]).toBe( + "FOLDER_ID", + ); + expect( + vi.mocked(driveServiceMock.Files.get).mock.calls[0][1], + ).toBeDefined(); + expect( + vi.mocked(driveServiceMock.Files.get).mock.calls[0][1], + ).toStrictEqual({ + userPermission: { role: true }, + }); + expect(vi.mocked(driveServiceMock.Files.remove).mock.calls).toHaveLength(0); + }, +); From da47021981f0059bb43ca5f2580e323cdbace96d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:17:47 +0100 Subject: [PATCH 16/23] Migrated moveFile test to vitest --- .../backend/move/moveFile.test.ts | 187 +++++++++++------- 1 file changed, 116 insertions(+), 71 deletions(-) rename {__tests__ => tests}/backend/move/moveFile.test.ts (50%) diff --git a/__tests__/backend/move/moveFile.test.ts b/tests/backend/move/moveFile.test.ts similarity index 50% rename from __tests__/backend/move/moveFile.test.ts rename to tests/backend/move/moveFile.test.ts index e1765f8f..a0ef6eb8 100644 --- a/__tests__/backend/move/moveFile.test.ts +++ b/tests/backend/move/moveFile.test.ts @@ -1,13 +1,12 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import * as copyFileComments from "../../../src/backend/move/copyFileComments"; import { moveFile_ } from "../../../src/backend/move/moveFile"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; -jest.mock("../../../src/backend/utils/MoveState"); -jest.mock("../../../src/backend/move/copyFileComments"); +vi.mock("../../../src/backend/utils/MoveState"); +vi.mock("../../../src/backend/move/copyFileComments"); test("moveFile works correctly with a file that can be moved directly", () => { interface UpdateFileOptions { @@ -17,7 +16,7 @@ test("moveFile works correctly with a file that can be moved directly", () => { } const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.update.mockReturnValueOnce({}); + vi.mocked(driveServiceMock.Files.update).mockReturnValueOnce({}); const state = new MoveState_( "SRC_BASE_ID", "DEST_BASE_ID", @@ -42,22 +41,30 @@ test("moveFile works correctly with a file that can be moved directly", () => { driveServiceMock, ); - expect(driveServiceMock.Files.update.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.update.mock.calls[0][1]).toContain( + expect(vi.mocked(driveServiceMock.Files.update).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls[0][1]).toContain( "SRC_FILE_ID", ); - expect(driveServiceMock.Files.update.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .addParents, + vi.mocked(driveServiceMock.Files.update).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).addParents, ).toContain("DEST_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .removeParents, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).removeParents, ).toContain("SRC_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .supportsAllDrives, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).supportsAllDrives, ).toBe(true); }); @@ -73,10 +80,10 @@ test("moveFile works correctly with a file that can be moved out of drive, yet c } const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.copy.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.copy).mockReturnValueOnce({ id: "DEST_FILE_ID", }); - driveServiceMock.Files.update.mockImplementation(() => { + vi.mocked(driveServiceMock.Files.update).mockImplementation(() => { throw new Error(); }); const state = new MoveState_( @@ -86,7 +93,7 @@ test("moveFile works correctly with a file that can be moved out of drive, yet c false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); moveFile_( { @@ -104,33 +111,47 @@ test("moveFile works correctly with a file that can be moved out of drive, yet c driveServiceMock, ); - expect(driveServiceMock.Files.update.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.update.mock.calls[0][1]).toContain( + expect(vi.mocked(driveServiceMock.Files.update).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls[0][1]).toContain( "SRC_FILE_ID", ); - expect(driveServiceMock.Files.update.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .addParents, + vi.mocked(driveServiceMock.Files.update).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).addParents, ).toContain("DEST_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .removeParents, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).removeParents, ).toContain("SRC_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .supportsAllDrives, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).supportsAllDrives, ).toBe(true); - expect(driveServiceMock.Files.copy.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents?.[0].id).toBe( - "DEST_PARENT_ID", + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents, + ).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents?.[0].id, + ).toBe("DEST_PARENT_ID"); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].title).toBe( + "FILE_NAME", ); - expect(driveServiceMock.Files.copy.mock.calls[0][0].title).toBe("FILE_NAME"); - expect(driveServiceMock.Files.copy.mock.calls[0][1]).toBe("SRC_FILE_ID"); - expect(driveServiceMock.Files.copy.mock.calls[0][2]).toBeDefined(); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][1]).toBe( + "SRC_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][2]).toBeDefined(); expect( - (driveServiceMock.Files.copy.mock.calls[0][3] as CopyFileOptions) + (vi.mocked(driveServiceMock.Files.copy).mock.calls[0][3] as CopyFileOptions) .supportsAllDrives, ).toBe(true); }); @@ -142,7 +163,7 @@ test("moveFile works correctly with a file that cannot be moved out of drive", ( } const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.copy.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.copy).mockReturnValueOnce({ id: "DEST_FILE_ID", }); const state = new MoveState_( @@ -152,7 +173,7 @@ test("moveFile works correctly with a file that cannot be moved out of drive", ( false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); moveFile_( { @@ -170,16 +191,22 @@ test("moveFile works correctly with a file that cannot be moved out of drive", ( driveServiceMock, ); - expect(driveServiceMock.Files.copy.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents?.[0].id).toBe( - "DEST_PARENT_ID", + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents, + ).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents?.[0].id, + ).toBe("DEST_PARENT_ID"); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].title).toBe( + "FILE_NAME", ); - expect(driveServiceMock.Files.copy.mock.calls[0][0].title).toBe("FILE_NAME"); - expect(driveServiceMock.Files.copy.mock.calls[0][1]).toBe("SRC_FILE_ID"); - expect(driveServiceMock.Files.copy.mock.calls[0][2]).toBeDefined(); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][1]).toBe( + "SRC_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][2]).toBeDefined(); expect( - (driveServiceMock.Files.copy.mock.calls[0][3] as CopyFileOptions) + (vi.mocked(driveServiceMock.Files.copy).mock.calls[0][3] as CopyFileOptions) .supportsAllDrives, ).toBe(true); }); @@ -192,7 +219,7 @@ test("moveFile works correctly with a file that can be moved directly with comme } const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.update.mockReturnValueOnce({}); + vi.mocked(driveServiceMock.Files.update).mockReturnValueOnce({}); const state = new MoveState_( "SRC_BASE_ID", "DEST_BASE_ID", @@ -217,22 +244,30 @@ test("moveFile works correctly with a file that can be moved directly with comme driveServiceMock, ); - expect(driveServiceMock.Files.update.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.update.mock.calls[0][1]).toContain( + expect(vi.mocked(driveServiceMock.Files.update).mock.calls).toHaveLength(1); + expect(vi.mocked(driveServiceMock.Files.update).mock.calls[0][1]).toContain( "SRC_FILE_ID", ); - expect(driveServiceMock.Files.update.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .addParents, + vi.mocked(driveServiceMock.Files.update).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).addParents, ).toContain("DEST_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .removeParents, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).removeParents, ).toContain("SRC_PARENT_ID"); expect( - (driveServiceMock.Files.update.mock.calls[0][3] as UpdateFileOptions) - .supportsAllDrives, + ( + vi.mocked(driveServiceMock.Files.update).mock + .calls[0][3] as UpdateFileOptions + ).supportsAllDrives, ).toBe(true); }); @@ -242,9 +277,11 @@ test("moveFile works correctly with a file that cannot be moved out of drive wit supportsAllDrives?: boolean; } - mocked(copyFileComments).copyFileComments_.mockReturnValueOnce(); + vi.mocked(copyFileComments).copyFileComments_.mockReturnValueOnce(); const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.copy.mockReturnValueOnce({ id: "DEST_FILE_ID" }); + vi.mocked(driveServiceMock.Files.copy).mockReturnValueOnce({ + id: "DEST_FILE_ID", + }); const state = new MoveState_( "SRC_BASE_ID", "DEST_BASE_ID", @@ -252,7 +289,7 @@ test("moveFile works correctly with a file that cannot be moved out of drive wit false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); moveFile_( { @@ -270,23 +307,31 @@ test("moveFile works correctly with a file that cannot be moved out of drive wit driveServiceMock, ); - expect(driveServiceMock.Files.copy.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents).toHaveLength(1); - expect(driveServiceMock.Files.copy.mock.calls[0][0].parents?.[0].id).toBe( - "DEST_PARENT_ID", + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents, + ).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].parents?.[0].id, + ).toBe("DEST_PARENT_ID"); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][0].title).toBe( + "FILE_NAME", ); - expect(driveServiceMock.Files.copy.mock.calls[0][0].title).toBe("FILE_NAME"); - expect(driveServiceMock.Files.copy.mock.calls[0][1]).toBe("SRC_FILE_ID"); - expect(driveServiceMock.Files.copy.mock.calls[0][2]).toBeDefined(); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][1]).toBe( + "SRC_FILE_ID", + ); + expect(vi.mocked(driveServiceMock.Files.copy).mock.calls[0][2]).toBeDefined(); expect( - (driveServiceMock.Files.copy.mock.calls[0][3] as CopyFileOptions) + (vi.mocked(driveServiceMock.Files.copy).mock.calls[0][3] as CopyFileOptions) .supportsAllDrives, ).toBe(true); - expect(mocked(copyFileComments).copyFileComments_.mock.calls).toHaveLength(1); - expect(mocked(copyFileComments).copyFileComments_.mock.calls[0][0]).toBe( + expect(vi.mocked(copyFileComments).copyFileComments_.mock.calls).toHaveLength( + 1, + ); + expect(vi.mocked(copyFileComments).copyFileComments_.mock.calls[0][0]).toBe( "SRC_FILE_ID", ); - expect(mocked(copyFileComments).copyFileComments_.mock.calls[0][1]).toBe( + expect(vi.mocked(copyFileComments).copyFileComments_.mock.calls[0][1]).toBe( "DEST_FILE_ID", ); }); @@ -295,10 +340,10 @@ test("moveFile fails gracefully on error", () => { expect.assertions(1); const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.copy.mockImplementation(() => { + vi.mocked(driveServiceMock.Files.copy).mockImplementation(() => { throw new Error("ERROR_MESAGE"); }); - driveServiceMock.Files.update.mockImplementation(() => { + vi.mocked(driveServiceMock.Files.update).mockImplementation(() => { throw new Error("ERROR_MESAGE"); }); const state = new MoveState_( @@ -308,7 +353,7 @@ test("moveFile fails gracefully on error", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => { + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => { expect(fn).toThrow("ERROR_MESAGE"); return null; From 7e373b0d1b599118072fabc22118082815e6a7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:25:27 +0100 Subject: [PATCH 17/23] Migrated moveFolder test to vitest --- .../backend/move/moveFolder.test.ts | 359 +++++++++--------- 1 file changed, 182 insertions(+), 177 deletions(-) rename {__tests__ => tests}/backend/move/moveFolder.test.ts (57%) diff --git a/__tests__/backend/move/moveFolder.test.ts b/tests/backend/move/moveFolder.test.ts similarity index 57% rename from __tests__/backend/move/moveFolder.test.ts rename to tests/backend/move/moveFolder.test.ts index 6cde1de1..3fa9bb6b 100644 --- a/__tests__/backend/move/moveFolder.test.ts +++ b/tests/backend/move/moveFolder.test.ts @@ -1,5 +1,4 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import * as folderManagement from "../../../src/backend/move/folderManagement"; import * as moveFile from "../../../src/backend/move/moveFile"; @@ -8,18 +7,18 @@ import * as resolveDestinationFolder from "../../../src/backend/move/resolveDest import { MoveState_ } from "../../../src/backend/utils/MoveState"; import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; -jest.mock("../../../src/backend/move/folderManagement"); -jest.mock("../../../src/backend/move/moveFile"); -jest.mock("../../../src/backend/move/resolveDestinationFolder"); -jest.mock("../../../src/backend/utils/MoveState"); +vi.mock("../../../src/backend/move/folderManagement"); +vi.mock("../../../src/backend/move/moveFile"); +vi.mock("../../../src/backend/move/resolveDestinationFolder"); +vi.mock("../../../src/backend/utils/MoveState"); test("moveFolder works correctly with an empty folder", () => { - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([]); + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([]); const driveServiceMock = mockedSafeDriveService(); const state = new MoveState_( "SRC_BASE_ID", @@ -28,7 +27,7 @@ test("moveFolder works correctly with an empty folder", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); const context = { destinationID: "DEST_ID", @@ -42,31 +41,32 @@ test("moveFolder works correctly with an empty folder", () => { expect(listFilesInFolder.mock.calls[0][0]).toBe("SRC_ID"); expect(listFoldersInFolder.mock.calls).toHaveLength(1); expect(listFoldersInFolder.mock.calls[0][0]).toBe("SRC_ID"); - expect(mocked(state).addPath.mock.calls).toHaveLength(0); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder moves files correctly", () => { - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE1_ID", - title: "FILE1_TITLE", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE2_ID", - title: "FILE2_TITLE", - }, - ]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([]); - const moveFileFn = mocked(moveFile) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE1_ID", + title: "FILE1_TITLE", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE2_ID", + title: "FILE2_TITLE", + }, + ]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([]); + const moveFileFn = vi + .mocked(moveFile) .moveFile_.mockReturnValueOnce() .mockReturnValueOnce(); const driveServiceMock = mockedSafeDriveService(); @@ -77,7 +77,7 @@ test("moveFolder moves files correctly", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); const context = { destinationID: "DEST_ID", @@ -99,31 +99,32 @@ test("moveFolder moves files correctly", () => { expect(moveFileFn.mock.calls[1][1]).toStrictEqual(state); expect(moveFileFn.mock.calls[1][2]).toStrictEqual(context); expect(moveFileFn.mock.calls[1][3]).toBe(false); - expect(mocked(state).addPath.mock.calls).toHaveLength(0); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder adds subfolders to the state correctly", () => { - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER1_ID", - title: "SUBFOLDER1_NAME", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER2_ID", - title: "SUBFOLDER2_NAME", - }, - ]); - const resolveDestinationFolderFn = mocked(resolveDestinationFolder) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER1_ID", + title: "SUBFOLDER1_NAME", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER2_ID", + title: "SUBFOLDER2_NAME", + }, + ]); + const resolveDestinationFolderFn = vi + .mocked(resolveDestinationFolder) .resolveDestinationFolder_.mockReturnValueOnce({ id: "DEST_SUBFOLDER1_ID" }) .mockReturnValueOnce({ id: "DEST_SUBFOLDER2_ID" }); const driveServiceMock = mockedSafeDriveService(); @@ -134,7 +135,7 @@ test("moveFolder adds subfolders to the state correctly", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); const context = { destinationID: "DEST_ID", @@ -160,51 +161,52 @@ test("moveFolder adds subfolders to the state correctly", () => { expect(resolveDestinationFolderFn.mock.calls[1][1]).toStrictEqual(state); expect(resolveDestinationFolderFn.mock.calls[1][2]).toStrictEqual(context); expect(resolveDestinationFolderFn.mock.calls[1][3]).toBe(false); - expect(mocked(state).addPath.mock.calls).toHaveLength(2); - expect(mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(2); + expect(vi.mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER1_NAME", ]); - expect(mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER2_NAME", ]); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder moves files correctly, even when listing folders throws", () => { expect.assertions(18); - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE1_ID", - title: "FILE1_TITLE", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE2_ID", - title: "FILE2_TITLE", - }, - ]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockImplementationOnce(() => { - throw new Error("ERROR_MESSAGE"); - }); - const moveFileFn = mocked(moveFile) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE1_ID", + title: "FILE1_TITLE", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE2_ID", + title: "FILE2_TITLE", + }, + ]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockImplementationOnce(() => { + throw new Error("ERROR_MESSAGE"); + }); + const moveFileFn = vi + .mocked(moveFile) .moveFile_.mockReturnValueOnce() .mockReturnValueOnce(); const driveServiceMock = mockedSafeDriveService(); @@ -215,7 +217,7 @@ test("moveFolder moves files correctly, even when listing folders throws", () => false, driveServiceMock, ); - mocked(state) + vi.mocked(state) .tryOrLog.mockImplementationOnce((_, fn) => fn()) .mockImplementationOnce((_, fn) => { expect(fn).toThrow("ERROR_MESSAGE"); @@ -243,35 +245,36 @@ test("moveFolder moves files correctly, even when listing folders throws", () => expect(moveFileFn.mock.calls[1][1]).toStrictEqual(state); expect(moveFileFn.mock.calls[1][2]).toStrictEqual(context); expect(moveFileFn.mock.calls[1][3]).toBe(false); - expect(mocked(state).addPath.mock.calls).toHaveLength(0); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder adds subfolders to the state correctly, even when listing files throws", () => { expect.assertions(24); - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockImplementationOnce(() => { - throw new Error("ERROR_MESSAGE"); - }); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER1_ID", - title: "SUBFOLDER1_NAME", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER2_ID", - title: "SUBFOLDER2_NAME", - }, - ]); - const resolveDestinationFolderFn = mocked(resolveDestinationFolder) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockImplementationOnce(() => { + throw new Error("ERROR_MESSAGE"); + }); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER1_ID", + title: "SUBFOLDER1_NAME", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER2_ID", + title: "SUBFOLDER2_NAME", + }, + ]); + const resolveDestinationFolderFn = vi + .mocked(resolveDestinationFolder) .resolveDestinationFolder_.mockReturnValueOnce({ id: "DEST_SUBFOLDER1_ID" }) .mockReturnValueOnce({ id: "DEST_SUBFOLDER2_ID" }); const driveServiceMock = mockedSafeDriveService(); @@ -282,7 +285,7 @@ test("moveFolder adds subfolders to the state correctly, even when listing files false, driveServiceMock, ); - mocked(state) + vi.mocked(state) .tryOrLog.mockImplementationOnce((_, fn) => { expect(fn).toThrow("ERROR_MESSAGE"); @@ -315,47 +318,48 @@ test("moveFolder adds subfolders to the state correctly, even when listing files expect(resolveDestinationFolderFn.mock.calls[1][1]).toStrictEqual(state); expect(resolveDestinationFolderFn.mock.calls[1][2]).toStrictEqual(context); expect(resolveDestinationFolderFn.mock.calls[1][3]).toBe(false); - expect(mocked(state).addPath.mock.calls).toHaveLength(2); - expect(mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(2); + expect(vi.mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER1_NAME", ]); - expect(mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER2_NAME", ]); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder passes copyComments correctly", () => { - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE1_ID", - title: "FILE1_TITLE", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "FILE2_ID", - title: "FILE2_TITLE", - }, - ]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([]); - const moveFileFn = mocked(moveFile) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE1_ID", + title: "FILE1_TITLE", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "FILE2_ID", + title: "FILE2_TITLE", + }, + ]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([]); + const moveFileFn = vi + .mocked(moveFile) .moveFile_.mockReturnValueOnce() .mockReturnValueOnce(); const driveServiceMock = mockedSafeDriveService(); @@ -366,7 +370,7 @@ test("moveFolder passes copyComments correctly", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); const context = { destinationID: "DEST_ID", @@ -389,31 +393,32 @@ test("moveFolder passes copyComments correctly", () => { expect(moveFileFn.mock.calls[1][1]).toStrictEqual(state); expect(moveFileFn.mock.calls[1][2]).toStrictEqual(context); expect(moveFileFn.mock.calls[1][3]).toBe(true); - expect(mocked(state).addPath.mock.calls).toHaveLength(0); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(0); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); test("moveFolder passes mergeFolders correctly", () => { - const listFilesInFolder = mocked( - folderManagement, - ).listFilesInFolder_.mockReturnValueOnce([]); - const listFoldersInFolder = mocked( - folderManagement, - ).listFoldersInFolder_.mockReturnValueOnce([ - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER1_ID", - title: "SUBFOLDER1_NAME", - }, - { - capabilities: { canMoveItemOutOfDrive: true }, - id: "SRC_SUBFOLDER2_ID", - title: "SUBFOLDER2_NAME", - }, - ]); - const resolveDestinationFolderFn = mocked(resolveDestinationFolder) + const listFilesInFolder = vi + .mocked(folderManagement) + .listFilesInFolder_.mockReturnValueOnce([]); + const listFoldersInFolder = vi + .mocked(folderManagement) + .listFoldersInFolder_.mockReturnValueOnce([ + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER1_ID", + title: "SUBFOLDER1_NAME", + }, + { + capabilities: { canMoveItemOutOfDrive: true }, + id: "SRC_SUBFOLDER2_ID", + title: "SUBFOLDER2_NAME", + }, + ]); + const resolveDestinationFolderFn = vi + .mocked(resolveDestinationFolder) .resolveDestinationFolder_.mockReturnValueOnce({ id: "DEST_SUBFOLDER1_ID" }) .mockReturnValueOnce({ id: "DEST_SUBFOLDER2_ID" }); const driveServiceMock = mockedSafeDriveService(); @@ -424,7 +429,7 @@ test("moveFolder passes mergeFolders correctly", () => { false, driveServiceMock, ); - mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); + vi.mocked(state).tryOrLog.mockImplementation((_, fn) => fn()); const context = { destinationID: "DEST_ID", @@ -451,24 +456,24 @@ test("moveFolder passes mergeFolders correctly", () => { expect(resolveDestinationFolderFn.mock.calls[1][1]).toStrictEqual(state); expect(resolveDestinationFolderFn.mock.calls[1][2]).toStrictEqual(context); expect(resolveDestinationFolderFn.mock.calls[1][3]).toBe(true); - expect(mocked(state).addPath.mock.calls).toHaveLength(2); - expect(mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); - expect(mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls).toHaveLength(2); + expect(vi.mocked(state).addPath.mock.calls[0][0]).toBe("SRC_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][1]).toBe("DEST_SUBFOLDER1_ID"); + expect(vi.mocked(state).addPath.mock.calls[0][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER1_NAME", ]); - expect(mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); - expect(mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ + expect(vi.mocked(state).addPath.mock.calls[1][0]).toBe("SRC_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][1]).toBe("DEST_SUBFOLDER2_ID"); + expect(vi.mocked(state).addPath.mock.calls[1][2]).toStrictEqual([ "PATH", "TO", "FOLDER", "SUBFOLDER2_NAME", ]); - expect(mocked(state).removePath.mock.calls).toHaveLength(1); - expect(mocked(state).removePath.mock.calls[0][0]).toBe(context); - expect(mocked(state).saveState.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls).toHaveLength(1); + expect(vi.mocked(state).removePath.mock.calls[0][0]).toBe(context); + expect(vi.mocked(state).saveState.mock.calls).toHaveLength(1); }); From a6f3f24134bd2366a9e0caa99f0d3ae826d32e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:30:10 +0100 Subject: [PATCH 18/23] Migrated resolveDestinationFolder test to vitest --- .../move/resolveDestinationFolder.test.ts | 143 ++++++++++-------- 1 file changed, 79 insertions(+), 64 deletions(-) rename {__tests__ => tests}/backend/move/resolveDestinationFolder.test.ts (61%) diff --git a/__tests__/backend/move/resolveDestinationFolder.test.ts b/tests/backend/move/resolveDestinationFolder.test.ts similarity index 61% rename from __tests__/backend/move/resolveDestinationFolder.test.ts rename to tests/backend/move/resolveDestinationFolder.test.ts index 0258d840..f5631104 100644 --- a/__tests__/backend/move/resolveDestinationFolder.test.ts +++ b/tests/backend/move/resolveDestinationFolder.test.ts @@ -1,13 +1,12 @@ -import { expect, jest, test } from "@jest/globals"; -import { mocked } from "jest-mock"; +import { expect, test, vi } from "vitest"; import * as folderManagement from "../../../src/backend/move/folderManagement"; import { resolveDestinationFolder_ } from "../../../src/backend/move/resolveDestinationFolder"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; -jest.mock("../../../src/backend/utils/MoveState"); -jest.mock("../../../src/backend/move/folderManagement"); +vi.mock("../../../src/backend/utils/MoveState"); +vi.mock("../../../src/backend/move/folderManagement"); test("resolveDestinationFolder corretly creates new folder", () => { interface InsertFileOptions { @@ -15,7 +14,7 @@ test("resolveDestinationFolder corretly creates new folder", () => { } const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.insert.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.insert).mockReturnValueOnce({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME", }); @@ -46,22 +45,26 @@ test("resolveDestinationFolder corretly creates new folder", () => { ), ).toStrictEqual({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME" }); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/vnd.google-apps.folder", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].parents).toStrictEqual([ - { id: "DEST_PARENT_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/vnd.google-apps.folder"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].parents, + ).toStrictEqual([{ id: "DEST_PARENT_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( "FOLDER_NAME", ); - expect(driveServiceMock.Files.insert.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.insert.mock.calls[0][3] as InsertFileOptions) - .supportsAllDrives, + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.insert).mock + .calls[0][3] as InsertFileOptions + ).supportsAllDrives, ).toBe(true); - expect(mocked(state).logError.mock.calls).toHaveLength(0); + expect(vi.mocked(state).logError.mock.calls).toHaveLength(0); }); test("resolveDestinationFolder corretly creates new folder when set not to merge folders, even when a folder with the same name exists", () => { @@ -69,7 +72,7 @@ test("resolveDestinationFolder corretly creates new folder when set not to merge supportsAllDrives?: boolean; } - mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ + vi.mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ { capabilities: { canMoveItemOutOfDrive: true }, id: "EXISTING_FOLDER_ID", @@ -77,7 +80,7 @@ test("resolveDestinationFolder corretly creates new folder when set not to merge }, ]); const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.insert.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.insert).mockReturnValueOnce({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME", }); @@ -108,22 +111,26 @@ test("resolveDestinationFolder corretly creates new folder when set not to merge ), ).toStrictEqual({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME" }); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/vnd.google-apps.folder", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].parents).toStrictEqual([ - { id: "DEST_PARENT_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/vnd.google-apps.folder"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].parents, + ).toStrictEqual([{ id: "DEST_PARENT_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( "FOLDER_NAME", ); - expect(driveServiceMock.Files.insert.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.insert.mock.calls[0][3] as InsertFileOptions) - .supportsAllDrives, + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.insert).mock + .calls[0][3] as InsertFileOptions + ).supportsAllDrives, ).toBe(true); - expect(mocked(state).logError.mock.calls).toHaveLength(0); + expect(vi.mocked(state).logError.mock.calls).toHaveLength(0); }); test("resolveDestinationFolder corretly creates new folder when set to merge folders, but there is no existing folder the same name", () => { @@ -131,9 +138,9 @@ test("resolveDestinationFolder corretly creates new folder when set to merge fol supportsAllDrives?: boolean; } - mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([]); + vi.mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([]); const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.insert.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.insert).mockReturnValueOnce({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME", }); @@ -164,26 +171,30 @@ test("resolveDestinationFolder corretly creates new folder when set to merge fol ), ).toStrictEqual({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME" }); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/vnd.google-apps.folder", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].parents).toStrictEqual([ - { id: "DEST_PARENT_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/vnd.google-apps.folder"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].parents, + ).toStrictEqual([{ id: "DEST_PARENT_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( "FOLDER_NAME", ); - expect(driveServiceMock.Files.insert.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.insert.mock.calls[0][3] as InsertFileOptions) - .supportsAllDrives, + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.insert).mock + .calls[0][3] as InsertFileOptions + ).supportsAllDrives, ).toBe(true); - expect(mocked(state).logError.mock.calls).toHaveLength(0); + expect(vi.mocked(state).logError.mock.calls).toHaveLength(0); }); test("resolveDestinationFolder corretly uses an existing folder when set to merge folders", () => { - mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ + vi.mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ { capabilities: { canMoveItemOutOfDrive: true }, id: "EXISTING_WRONG_FOLDER1_ID", @@ -232,8 +243,8 @@ test("resolveDestinationFolder corretly uses an existing folder when set to merg title: "FOLDER_NAME", }); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(0); - expect(mocked(state).logError.mock.calls).toHaveLength(0); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(0); + expect(vi.mocked(state).logError.mock.calls).toHaveLength(0); }); test("resolveDestinationFolder fails gracefully on multiple existing folders with the same name", () => { @@ -241,7 +252,7 @@ test("resolveDestinationFolder fails gracefully on multiple existing folders wit supportsAllDrives?: boolean; } - mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ + vi.mocked(folderManagement).listFoldersInFolder_.mockReturnValueOnce([ { capabilities: { canMoveItemOutOfDrive: true }, id: "EXISTING_WRONG_FOLDER1_ID", @@ -264,7 +275,7 @@ test("resolveDestinationFolder fails gracefully on multiple existing folders wit }, ]); const driveServiceMock = mockedSafeDriveService(); - driveServiceMock.Files.insert.mockReturnValueOnce({ + vi.mocked(driveServiceMock.Files.insert).mockReturnValueOnce({ id: "NEWLY_CREATED_FOLDER_ID", title: "FOLDER_NAME", }); @@ -298,27 +309,31 @@ test("resolveDestinationFolder fails gracefully on multiple existing folders wit title: "FOLDER_NAME", }); - expect(driveServiceMock.Files.insert.mock.calls).toHaveLength(1); - expect(driveServiceMock.Files.insert.mock.calls[0][0].mimeType).toBe( - "application/vnd.google-apps.folder", - ); - expect(driveServiceMock.Files.insert.mock.calls[0][0].parents).toStrictEqual([ - { id: "DEST_PARENT_ID" }, - ]); - expect(driveServiceMock.Files.insert.mock.calls[0][0].title).toBe( + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls).toHaveLength(1); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].mimeType, + ).toBe("application/vnd.google-apps.folder"); + expect( + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].parents, + ).toStrictEqual([{ id: "DEST_PARENT_ID" }]); + expect(vi.mocked(driveServiceMock.Files.insert).mock.calls[0][0].title).toBe( "FOLDER_NAME", ); - expect(driveServiceMock.Files.insert.mock.calls[0][3]).toBeDefined(); expect( - (driveServiceMock.Files.insert.mock.calls[0][3] as InsertFileOptions) - .supportsAllDrives, + vi.mocked(driveServiceMock.Files.insert).mock.calls[0][3], + ).toBeDefined(); + expect( + ( + vi.mocked(driveServiceMock.Files.insert).mock + .calls[0][3] as InsertFileOptions + ).supportsAllDrives, ).toBe(true); - expect(mocked(state).logError.mock.calls).toHaveLength(1); - expect(mocked(state).logError.mock.calls[0][0]).toStrictEqual([ + expect(vi.mocked(state).logError.mock.calls).toHaveLength(1); + expect(vi.mocked(state).logError.mock.calls[0][0]).toStrictEqual([ "PATH", "TO", "FOLDER", "FOLDER_NAME", ]); - expect(mocked(state).logError.mock.calls[0][1]).not.toBe(""); + expect(vi.mocked(state).logError.mock.calls[0][1]).not.toBe(""); }); From f88b0279eb4bfc457f24bfee5877fe3f3adf161e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:41:48 +0100 Subject: [PATCH 19/23] Separated test-utils between frontend and backend --- __tests__/frontend/basic.spec.ts | 2 +- __tests__/frontend/configuration.spec.ts | 2 +- .../destination-selection-api-error.spec.ts | 2 +- ...-selection-invalid-parameter-error.spec.ts | 2 +- ...tination-selection-unhandled-error.spec.ts | 2 +- ...estination-selection-unknown-error.spec.ts | 2 +- __tests__/frontend/move-api-error.spec.ts | 2 +- .../frontend/move-folders-equal-error.spec.ts | 2 +- .../move-invalid-parameter-error.spec.ts | 2 +- .../move-repeat-after-timeout.spec.ts | 2 +- .../frontend/move-unhandled-error.spec.ts | 2 +- __tests__/frontend/move-unknown-error.spec.ts | 2 +- __tests__/frontend/navigation.spec.ts | 2 +- __tests__/frontend/non-empty.spec.ts | 2 +- .../source-destination-selection.spec.ts | 2 +- .../source-selection-api-error.spec.ts | 2 +- ...-selection-invalid-parameter-error.spec.ts | 2 +- .../source-selection-unhandled-error.spec.ts | 2 +- .../source-selection-unknown-error.spec.ts | 2 +- .../frontend/success-with-errors.spec.ts | 2 +- .../test-utils/stub-endpoints.ts | 0 __tests__/test-utils/DriveBackedValue-stub.ts | 25 - __tests__/test-utils/MoveState-stub.ts | 20 - __tests__/test-utils/SafeDriveService-stub.ts | 104 ---- __tests__/test-utils/gas-stubs.ts | 470 ------------------ eslint.config.js | 7 +- tests/backend/doGet.test.ts | 2 +- tests/backend/listFolders.test.ts | 4 +- tests/backend/listSharedDrives.test.ts | 2 +- tests/backend/move.test.ts | 2 +- tests/backend/move/copyFileComments.test.ts | 2 +- tests/backend/move/folderManagement.test.ts | 2 +- tests/backend/move/moveFile.test.ts | 2 +- tests/backend/move/moveFolder.test.ts | 2 +- .../move/resolveDestinationFolder.test.ts | 2 +- .../test-utils/DriveBackedValue-stub.ts | 6 +- .../test-utils/MoveState-stub.ts | 2 +- .../test-utils/SafeDriveService-stub.ts | 6 +- tests/{ => backend}/test-utils/gas-stubs.ts | 0 tests/backend/utils/DriveBackedValue.test.ts | 4 +- tests/backend/utils/MoveState.test.ts | 4 +- tests/backend/utils/SafeDriveService.test.ts | 2 +- .../SafeCommentsCollection.test.ts | 2 +- .../SafeDrivesCollection.test.ts | 2 +- .../SafeFilesCollection.test.ts | 5 +- 45 files changed, 47 insertions(+), 672 deletions(-) rename __tests__/{ => frontend}/test-utils/stub-endpoints.ts (100%) delete mode 100644 __tests__/test-utils/DriveBackedValue-stub.ts delete mode 100644 __tests__/test-utils/MoveState-stub.ts delete mode 100644 __tests__/test-utils/SafeDriveService-stub.ts delete mode 100644 __tests__/test-utils/gas-stubs.ts rename tests/{ => backend}/test-utils/DriveBackedValue-stub.ts (65%) rename tests/{ => backend}/test-utils/MoveState-stub.ts (85%) rename tests/{ => backend}/test-utils/SafeDriveService-stub.ts (93%) rename tests/{ => backend}/test-utils/gas-stubs.ts (100%) diff --git a/__tests__/frontend/basic.spec.ts b/__tests__/frontend/basic.spec.ts index 0d610aa1..7580bd57 100644 --- a/__tests__/frontend/basic.spec.ts +++ b/__tests__/frontend/basic.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with basic configuration", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/configuration.spec.ts b/__tests__/frontend/configuration.spec.ts index 81c12151..8a35252a 100644 --- a/__tests__/frontend/configuration.spec.ts +++ b/__tests__/frontend/configuration.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with copy configuration", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/destination-selection-api-error.spec.ts b/__tests__/frontend/destination-selection-api-error.spec.ts index 31444ef4..6f294bde 100644 --- a/__tests__/frontend/destination-selection-api-error.spec.ts +++ b/__tests__/frontend/destination-selection-api-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/destination-selection-invalid-parameter-error.spec.ts b/__tests__/frontend/destination-selection-invalid-parameter-error.spec.ts index 7c83ee7f..2cd832ca 100644 --- a/__tests__/frontend/destination-selection-invalid-parameter-error.spec.ts +++ b/__tests__/frontend/destination-selection-invalid-parameter-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/destination-selection-unhandled-error.spec.ts b/__tests__/frontend/destination-selection-unhandled-error.spec.ts index fa529111..6072b644 100644 --- a/__tests__/frontend/destination-selection-unhandled-error.spec.ts +++ b/__tests__/frontend/destination-selection-unhandled-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/destination-selection-unknown-error.spec.ts b/__tests__/frontend/destination-selection-unknown-error.spec.ts index 749df106..a081d6db 100644 --- a/__tests__/frontend/destination-selection-unknown-error.spec.ts +++ b/__tests__/frontend/destination-selection-unknown-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/move-api-error.spec.ts b/__tests__/frontend/move-api-error.spec.ts index a73f667f..b0d56a3f 100644 --- a/__tests__/frontend/move-api-error.spec.ts +++ b/__tests__/frontend/move-api-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with an API error", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/move-folders-equal-error.spec.ts b/__tests__/frontend/move-folders-equal-error.spec.ts index 203e5a0c..80077c28 100644 --- a/__tests__/frontend/move-folders-equal-error.spec.ts +++ b/__tests__/frontend/move-folders-equal-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with source and destination folders being equal", async ({ page, diff --git a/__tests__/frontend/move-invalid-parameter-error.spec.ts b/__tests__/frontend/move-invalid-parameter-error.spec.ts index ef1b6105..59be0544 100644 --- a/__tests__/frontend/move-invalid-parameter-error.spec.ts +++ b/__tests__/frontend/move-invalid-parameter-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with an API error", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/move-repeat-after-timeout.spec.ts b/__tests__/frontend/move-repeat-after-timeout.spec.ts index c8f7f1f1..227609c0 100644 --- a/__tests__/frontend/move-repeat-after-timeout.spec.ts +++ b/__tests__/frontend/move-repeat-after-timeout.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with an unhandled move error", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/move-unhandled-error.spec.ts b/__tests__/frontend/move-unhandled-error.spec.ts index 2a2f3b8d..99359f78 100644 --- a/__tests__/frontend/move-unhandled-error.spec.ts +++ b/__tests__/frontend/move-unhandled-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with an unhandled move error", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/move-unknown-error.spec.ts b/__tests__/frontend/move-unknown-error.spec.ts index a5023d4e..9bb98e3b 100644 --- a/__tests__/frontend/move-unknown-error.spec.ts +++ b/__tests__/frontend/move-unknown-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with an unknown move error", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/navigation.spec.ts b/__tests__/frontend/navigation.spec.ts index 64e034be..605b8460 100644 --- a/__tests__/frontend/navigation.spec.ts +++ b/__tests__/frontend/navigation.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with basic configuration", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/non-empty.spec.ts b/__tests__/frontend/non-empty.spec.ts index 17ea65fa..4689ab8a 100644 --- a/__tests__/frontend/non-empty.spec.ts +++ b/__tests__/frontend/non-empty.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with non-empty destination folder", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/source-destination-selection.spec.ts b/__tests__/frontend/source-destination-selection.spec.ts index 2dd0c67f..6e9c090a 100644 --- a/__tests__/frontend/source-destination-selection.spec.ts +++ b/__tests__/frontend/source-destination-selection.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works with folder selection", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/frontend/source-selection-api-error.spec.ts b/__tests__/frontend/source-selection-api-error.spec.ts index 8510dd2c..0bb3bce2 100644 --- a/__tests__/frontend/source-selection-api-error.spec.ts +++ b/__tests__/frontend/source-selection-api-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/source-selection-invalid-parameter-error.spec.ts b/__tests__/frontend/source-selection-invalid-parameter-error.spec.ts index ba2386b0..830e3e49 100644 --- a/__tests__/frontend/source-selection-invalid-parameter-error.spec.ts +++ b/__tests__/frontend/source-selection-invalid-parameter-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles invalid parameter errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/source-selection-unhandled-error.spec.ts b/__tests__/frontend/source-selection-unhandled-error.spec.ts index 48d5e684..0877768e 100644 --- a/__tests__/frontend/source-selection-unhandled-error.spec.ts +++ b/__tests__/frontend/source-selection-unhandled-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/source-selection-unknown-error.spec.ts b/__tests__/frontend/source-selection-unknown-error.spec.ts index b823510f..49d62ac1 100644 --- a/__tests__/frontend/source-selection-unknown-error.spec.ts +++ b/__tests__/frontend/source-selection-unknown-error.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("handles raw errors in source folder selection gracefully", async ({ page, diff --git a/__tests__/frontend/success-with-errors.spec.ts b/__tests__/frontend/success-with-errors.spec.ts index a2f20ec2..4b6ca378 100644 --- a/__tests__/frontend/success-with-errors.spec.ts +++ b/__tests__/frontend/success-with-errors.spec.ts @@ -1,6 +1,6 @@ import { expect, test } from "playwright-test-coverage"; -import { setup } from "../test-utils/stub-endpoints"; +import { setup } from "./test-utils/stub-endpoints"; test("works and displays moving errors", async ({ page }) => { await page.goto("/"); diff --git a/__tests__/test-utils/stub-endpoints.ts b/__tests__/frontend/test-utils/stub-endpoints.ts similarity index 100% rename from __tests__/test-utils/stub-endpoints.ts rename to __tests__/frontend/test-utils/stub-endpoints.ts diff --git a/__tests__/test-utils/DriveBackedValue-stub.ts b/__tests__/test-utils/DriveBackedValue-stub.ts deleted file mode 100644 index 2a8113e7..00000000 --- a/__tests__/test-utils/DriveBackedValue-stub.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { MockedObject } from "jest-mock"; - -import { jest } from "@jest/globals"; - -import type { DriveBackedValue_ } from "../../src/backend/utils/DriveBackedValue"; -import type { MoveContext } from "../../src/interfaces/MoveContext"; -import type { MoveError } from "../../src/interfaces/MoveError"; - -export function mockedDriveBackedValue(): MockedObject< - DriveBackedValue_<{ - errors: Array; - pathsToProcess: Array; - }> -> { - return { - deleteValue: jest.fn(), - loadValue: jest.fn(), - saveValue: jest.fn(), - } as unknown as MockedObject< - DriveBackedValue_<{ - errors: Array; - pathsToProcess: Array; - }> - >; -} diff --git a/__tests__/test-utils/MoveState-stub.ts b/__tests__/test-utils/MoveState-stub.ts deleted file mode 100644 index 2ff19b18..00000000 --- a/__tests__/test-utils/MoveState-stub.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { MockedObject } from "jest-mock"; - -import { jest } from "@jest/globals"; - -import type { MoveState_ } from "../../src/backend/utils/MoveState"; - -export function mockedMoveState(): MockedObject { - return { - addPath: jest.fn(), - destroyState: jest.fn(), - getErrors: jest.fn(), - getNextPath: jest.fn(), - isNull: jest.fn(), - loadState: jest.fn(), - logError: jest.fn(), - removePath: jest.fn(), - saveState: jest.fn(), - tryOrLog: jest.fn(), - } as unknown as MockedObject; -} diff --git a/__tests__/test-utils/SafeDriveService-stub.ts b/__tests__/test-utils/SafeDriveService-stub.ts deleted file mode 100644 index 552b6e6d..00000000 --- a/__tests__/test-utils/SafeDriveService-stub.ts +++ /dev/null @@ -1,104 +0,0 @@ -import type { MockedObject } from "jest-mock"; - -import { jest } from "@jest/globals"; - -import type { DeepKeyof } from "../../src/backend/utils/DeepKeyof"; -import type { DeepPick } from "../../src/backend/utils/DeepPick"; -import type { - SafeComment, - SafeCommentList, - SafeDriveList, - SafeDriveService_, - SafeFile, - SafeFileList, -} from "../../src/backend/utils/SafeDriveService"; - -import { mockedRepliesCollection } from "./gas-stubs"; - -export function mockedSafeDriveService< - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- No other way to pass F to jest.fn() - F extends DeepKeyof, ->(): MockedObject { - return { - Comments: { - insert: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Comment, - fileId: string, - ) => SafeComment - >(), - list: jest.fn< - (fileId: string, optionalArgs: Record) => SafeCommentList - >(), - }, - Drives: { - list: jest.fn< - ( - fields: F | null, - optionalArgs?: { - maxResults?: number; - orderBy?: string; - pageToken?: string; - }, - ) => SafeDriveList - >(), - }, - Files: { - copy: jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fileId: string, - fields: F | null, - optionalArgs?: { supportsAllDrives?: boolean }, - ) => DeepPick - >(), - get: jest.fn< - ( - fileId: string, - fields: F | null, - optionalArgs?: { alt?: string }, - ) => DeepPick - >(), - insert: jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fields: F | null, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Required by the Drive API - mediaData?: any, - optionalArgs?: { - supportsAllDrives?: boolean; - }, - ) => DeepPick - >(), - list: jest.fn< - ( - fields: F | null, - optionalArgs?: { - includeItemsFromAllDrives?: boolean; - maxResults?: number; - pageToken?: string; - q?: string; - supportsAllDrives?: boolean; - }, - ) => SafeFileList - >(), - remove: jest.fn<(fileId: string) => void>(), - update: jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fileId: string, - fields: F | null, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Required by the Drive API - mediaData?: any, - optionalArgs?: { - addParents?: string; - removeParents?: string; - supportsAllDrives?: boolean; - }, - ) => DeepPick - >(), - }, - Replies: mockedRepliesCollection(), - } as unknown as MockedObject; -} diff --git a/__tests__/test-utils/gas-stubs.ts b/__tests__/test-utils/gas-stubs.ts deleted file mode 100644 index 91cf6884..00000000 --- a/__tests__/test-utils/gas-stubs.ts +++ /dev/null @@ -1,470 +0,0 @@ -import { jest } from "@jest/globals"; - -export function mockedCommentsCollection(): GoogleAppsScript.Drive.Collection.CommentsCollection { - return { - get: jest.fn< - ( - fileId: string, - commentId: string, - ) => GoogleAppsScript.Drive.Schema.Comment - >(), - insert: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Comment, - fileId: string, - ) => GoogleAppsScript.Drive.Schema.Comment - >(), - list: jest.fn< - (fileId: string) => GoogleAppsScript.Drive.Schema.CommentList - >(), - patch: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Comment, - fileId: string, - commentId: string, - ) => GoogleAppsScript.Drive.Schema.Comment - >(), - remove: jest.fn<(fileId: string, commentId: string) => void>(), - update: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Comment, - fileId: string, - commentId: string, - ) => GoogleAppsScript.Drive.Schema.Comment - >(), - }; -} - -export function mockedDrive(): GoogleAppsScript.Drive_v2 { - return { - newChannel: jest.fn<() => GoogleAppsScript.Drive.Schema.Channel>(), - newChildReference: - jest.fn<() => GoogleAppsScript.Drive.Schema.ChildReference>(), - newComment: jest.fn<() => GoogleAppsScript.Drive.Schema.Comment>(), - newCommentContext: - jest.fn<() => GoogleAppsScript.Drive.Schema.CommentContext>(), - newCommentReply: - jest.fn<() => GoogleAppsScript.Drive.Schema.CommentReply>(), - newDrive: jest.fn<() => GoogleAppsScript.Drive.Schema.Drive>(), - newDriveBackgroundImageFile: - jest.fn<() => GoogleAppsScript.Drive.Schema.DriveBackgroundImageFile>(), - newDriveCapabilities: - jest.fn<() => GoogleAppsScript.Drive.Schema.DriveCapabilities>(), - newDriveRestrictions: - jest.fn<() => GoogleAppsScript.Drive.Schema.DriveRestrictions>(), - newFile: jest.fn<() => GoogleAppsScript.Drive.Schema.File>(), - newFileCapabilities: - jest.fn<() => GoogleAppsScript.Drive.Schema.FileCapabilities>(), - newFileImageMediaMetadata: - jest.fn<() => GoogleAppsScript.Drive.Schema.FileImageMediaMetadata>(), - newFileImageMediaMetadataLocation: - jest.fn< - () => GoogleAppsScript.Drive.Schema.FileImageMediaMetadataLocation - >(), - newFileIndexableText: - jest.fn<() => GoogleAppsScript.Drive.Schema.FileIndexableText>(), - newFileLabels: jest.fn<() => GoogleAppsScript.Drive.Schema.FileLabels>(), - newFileThumbnail: - jest.fn<() => GoogleAppsScript.Drive.Schema.FileThumbnail>(), - newFileVideoMediaMetadata: - jest.fn<() => GoogleAppsScript.Drive.Schema.FileVideoMediaMetadata>(), - newParentReference: - jest.fn<() => GoogleAppsScript.Drive.Schema.ParentReference>(), - newPermission: jest.fn<() => GoogleAppsScript.Drive.Schema.Permission>(), - newPermissionPermissionDetails: - jest.fn< - () => GoogleAppsScript.Drive.Schema.PermissionPermissionDetails - >(), - newPermissionTeamDrivePermissionDetails: - jest.fn< - () => GoogleAppsScript.Drive.Schema.PermissionTeamDrivePermissionDetails - >(), - newProperty: jest.fn<() => GoogleAppsScript.Drive.Schema.Property>(), - newRevision: jest.fn<() => GoogleAppsScript.Drive.Schema.Revision>(), - newTeamDrive: jest.fn<() => GoogleAppsScript.Drive.Schema.TeamDrive>(), - newTeamDriveBackgroundImageFile: - jest.fn< - () => GoogleAppsScript.Drive.Schema.TeamDriveBackgroundImageFile - >(), - newTeamDriveCapabilities: - jest.fn<() => GoogleAppsScript.Drive.Schema.TeamDriveCapabilities>(), - newTeamDriveRestrictions: - jest.fn<() => GoogleAppsScript.Drive.Schema.TeamDriveRestrictions>(), - newUser: jest.fn<() => GoogleAppsScript.Drive.Schema.User>(), - newUserPicture: jest.fn<() => GoogleAppsScript.Drive.Schema.UserPicture>(), - }; -} - -export function mockedDrivesCollection(): GoogleAppsScript.Drive.Collection.DrivesCollection { - return { - get: jest.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), - hide: jest.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), - insert: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Drive, - requestId: string, - ) => GoogleAppsScript.Drive.Schema.Drive - >(), - list: jest.fn<() => GoogleAppsScript.Drive.Schema.DriveList>(), - remove: jest.fn<(driveId: string) => void>(), - unhide: jest.fn<(driveId: string) => GoogleAppsScript.Drive.Schema.Drive>(), - update: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Drive, - driveId: string, - ) => GoogleAppsScript.Drive.Schema.Drive - >(), - }; -} - -export function mockedFilesCollection(): GoogleAppsScript.Drive.Collection.FilesCollection { - return { - copy: jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fileId: string, - ) => GoogleAppsScript.Drive.Schema.File - >(), - emptyTrash: jest.fn<() => void>(), - export: jest.fn<(fileId: string, mimeType: string) => void>(), - generateIds: jest.fn<() => GoogleAppsScript.Drive.Schema.GeneratedIds>(), - get: jest.fn() as { - ( - fileId: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From upstream types - optionalArgs?: Record & { alt: "media" }, - ): string; - ( - fileId: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From upstream types - optionalArgs?: Record, - ): GoogleAppsScript.Drive.Schema.File; - }, - insert: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - mediaData?: Blob, - ) => GoogleAppsScript.Drive.Schema.File - >(), - list: jest.fn<() => GoogleAppsScript.Drive.Schema.FileList>(), - patch: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fileId: string, - ) => GoogleAppsScript.Drive.Schema.File - >(), - remove: jest.fn<(fileId: string) => void>(), - touch: jest.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), - trash: jest.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), - untrash: jest.fn<(fileId: string) => GoogleAppsScript.Drive.Schema.File>(), - update: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.File, - fileId: string, - mediaData?: Blob, - ) => GoogleAppsScript.Drive.Schema.File - >(), - watch: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.Channel, - fileId: string, - ) => GoogleAppsScript.Drive.Schema.Channel - >(), - }; -} - -export function mockedHtmlOutput(): GoogleAppsScript.HTML.HtmlOutput { - return { - addMetaTag: - jest.fn< - (name: string, content: string) => GoogleAppsScript.HTML.HtmlOutput - >(), - append: - jest.fn<(addedContent: string) => GoogleAppsScript.HTML.HtmlOutput>(), - appendUntrusted: - jest.fn<(addedContent: string) => GoogleAppsScript.HTML.HtmlOutput>(), - asTemplate: jest.fn<() => GoogleAppsScript.HTML.HtmlTemplate>(), - clear: jest.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), - getAs: jest.fn<(contentType: string) => GoogleAppsScript.Base.Blob>(), - getBlob: jest.fn<() => GoogleAppsScript.Base.Blob>(), - getContent: jest.fn<() => string>(), - getFaviconUrl: jest.fn<() => string>(), - getHeight: jest.fn<() => GoogleAppsScript.Integer>(), - getMetaTags: - jest.fn<() => Array>(), - getTitle: jest.fn<() => string>(), - getWidth: jest.fn<() => GoogleAppsScript.Integer>(), - setContent: - jest.fn<(content: string) => GoogleAppsScript.HTML.HtmlOutput>(), - setFaviconUrl: - jest.fn<(iconUrl: string) => GoogleAppsScript.HTML.HtmlOutput>(), - setHeight: - jest.fn< - (height: GoogleAppsScript.Integer) => GoogleAppsScript.HTML.HtmlOutput - >(), - setSandboxMode: - jest.fn< - ( - mode: GoogleAppsScript.HTML.SandboxMode, - ) => GoogleAppsScript.HTML.HtmlOutput - >(), - setTitle: jest.fn<(title: string) => GoogleAppsScript.HTML.HtmlOutput>(), - setWidth: - jest.fn< - (width: GoogleAppsScript.Integer) => GoogleAppsScript.HTML.HtmlOutput - >(), - setXFrameOptionsMode: - jest.fn< - ( - mode: GoogleAppsScript.HTML.XFrameOptionsMode, - ) => GoogleAppsScript.HTML.HtmlOutput - >(), - }; -} - -export function mockedHtmlService(): GoogleAppsScript.HTML.HtmlService { - return { - createHtmlOutput: jest.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), - createHtmlOutputFromFile: - jest.fn<(filename: string) => GoogleAppsScript.HTML.HtmlOutput>(), - createTemplate: - jest.fn< - ( - arg: GoogleAppsScript.Base.BlobSource | string, - ) => GoogleAppsScript.HTML.HtmlTemplate - >(), - createTemplateFromFile: jest.fn<() => GoogleAppsScript.HTML.HtmlTemplate>(), - getUserAgent: jest.fn<() => string>(), - SandboxMode: 0 as unknown as typeof GoogleAppsScript.HTML.SandboxMode, - XFrameOptionsMode: - 0 as unknown as typeof GoogleAppsScript.HTML.XFrameOptionsMode, - }; -} - -export function mockedHtmlTemplate(): GoogleAppsScript.HTML.HtmlTemplate { - return { - evaluate: jest.fn<() => GoogleAppsScript.HTML.HtmlOutput>(), - getCode: jest.fn<() => string>(), - getCodeWithComments: jest.fn<() => string>(), - getRawContent: jest.fn<() => string>(), - }; -} - -export function mockedRepliesCollection(): GoogleAppsScript.Drive.Collection.RepliesCollection { - return { - get: jest.fn< - ( - fileId: string, - commentId: string, - replyId: string, - ) => GoogleAppsScript.Drive.Schema.CommentReply - >(), - insert: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.CommentReply, - fileId: string, - commentId: string, - ) => GoogleAppsScript.Drive.Schema.CommentReply - >(), - list: jest.fn< - ( - fileId: string, - commentId: string, - ) => GoogleAppsScript.Drive.Schema.CommentReplyList - >(), - patch: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.CommentReply, - fileId: string, - commentId: string, - replyId: string, - ) => GoogleAppsScript.Drive.Schema.CommentReply - >(), - remove: - jest.fn<(fileId: string, commentId: string, replyId: string) => void>(), - update: - jest.fn< - ( - resource: GoogleAppsScript.Drive.Schema.CommentReply, - fileId: string, - commentId: string, - replyId: string, - ) => GoogleAppsScript.Drive.Schema.CommentReply - >(), - }; -} - -export function mockedSession(): GoogleAppsScript.Base.Session { - return { - getActiveUser: jest.fn<() => GoogleAppsScript.Base.User>(), - getActiveUserLocale: jest.fn<() => string>(), - getEffectiveUser: jest.fn<() => GoogleAppsScript.Base.User>(), - getScriptTimeZone: jest.fn<() => string>(), - getTemporaryActiveUserKey: jest.fn<() => string>(), - getTimeZone: jest.fn<() => string>(), - getUser: jest.fn<() => GoogleAppsScript.Base.User>(), - }; -} - -export function mockedUtilities(): GoogleAppsScript.Utilities.Utilities { - return { - base64Decode: - jest.fn< - ( - encoded: string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - base64DecodeWebSafe: - jest.fn< - ( - encoded: string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - base64Encode: - jest.fn< - ( - data: Array | string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => string - >(), - base64EncodeWebSafe: - jest.fn< - ( - data: Array | string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => string - >(), - Charset: { US_ASCII: 0, UTF_8: 1 }, - computeDigest: - jest.fn< - ( - algorithm: GoogleAppsScript.Utilities.DigestAlgorithm, - value: Array | string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - computeHmacSha256Signature: - jest.fn< - ( - value: Array | string, - key: Array | string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - computeHmacSignature: - jest.fn< - ( - algorithm: GoogleAppsScript.Utilities.MacAlgorithm, - value: Array | string, - key: Array | string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - computeRsaSha1Signature: - jest.fn< - ( - value: string, - key: string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - computeRsaSha256Signature: - jest.fn< - ( - value: string, - key: string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - computeRsaSignature: - jest.fn< - ( - algorithm: GoogleAppsScript.Utilities.RsaAlgorithm, - value: string, - key: string, - charset?: GoogleAppsScript.Utilities.Charset, - ) => Array - >(), - DigestAlgorithm: { - MD2: 0, - MD5: 1, - SHA_1: 2, - SHA_256: 3, - SHA_384: 4, - SHA_512: 5, - }, - formatDate: - jest.fn< - ( - date: GoogleAppsScript.Base.Date, - timeZone: string, - format: string, - ) => string - >(), - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types - formatString: jest.fn<(template: string, ...args: Array) => string>(), - getUuid: jest.fn<() => string>(), - gzip: jest.fn< - ( - blob: GoogleAppsScript.Base.BlobSource, - name?: string, - ) => GoogleAppsScript.Base.Blob - >(), - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types - jsonParse: jest.fn<(jsonString: string) => any>(), - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- From Google apps script types - jsonStringify: jest.fn<(obj: any) => string>(), - MacAlgorithm: { - HMAC_MD5: 0, - HMAC_SHA_1: 1, - HMAC_SHA_256: 2, - HMAC_SHA_384: 3, - HMAC_SHA_512: 4, - }, - newBlob: - jest.fn< - ( - data: Array | string, - contentType?: string, - name?: string, - ) => GoogleAppsScript.Base.Blob - >(), - parseCsv: - jest.fn< - (csv: string, delimiter?: GoogleAppsScript.Char) => Array> - >(), - parseDate: - jest.fn<(date: string, timeZone: string, format: string) => Date>(), - RsaAlgorithm: { RSA_SHA_1: 0, RSA_SHA_256: 1 }, - sleep: jest.fn<(milliseconds: GoogleAppsScript.Integer) => void>(), - ungzip: - jest.fn< - (blob: GoogleAppsScript.Base.BlobSource) => GoogleAppsScript.Base.Blob - >(), - unzip: - jest.fn< - ( - blob: GoogleAppsScript.Base.BlobSource, - ) => Array - >(), - zip: jest.fn< - ( - blobs: Array, - name?: string, - ) => GoogleAppsScript.Base.Blob - >(), - }; -} diff --git a/eslint.config.js b/eslint.config.js index 6138070e..e318c39d 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -218,7 +218,7 @@ export default tseslint.config( }, }, { - files: ["tests/**/*.test.ts", "tests/test-utils/gas-stubs.ts"], + files: ["tests/**/*.ts"], ...vitest.configs.recommended, rules: { ...vitest.configs.recommended.rules, @@ -308,10 +308,7 @@ export default tseslint.config( }, { ...playwright.configs["flat/recommended"], - files: [ - "__tests__/frontend/*.ts", - "__tests__/test-utils/stub-endpoints.ts", - ], + files: ["__tests__/frontend/**/*.ts"], rules: { ...playwright.configs["flat/recommended"].rules, "playwright/no-commented-out-tests": "error", diff --git a/tests/backend/doGet.test.ts b/tests/backend/doGet.test.ts index b813f0d9..8b037e54 100644 --- a/tests/backend/doGet.test.ts +++ b/tests/backend/doGet.test.ts @@ -5,7 +5,7 @@ import { mockedHtmlOutput, mockedHtmlService, mockedHtmlTemplate, -} from "../test-utils/gas-stubs"; +} from "./test-utils/gas-stubs"; test("doGet works correctly", () => { const outputWithTitle = mockedHtmlOutput(); diff --git a/tests/backend/listFolders.test.ts b/tests/backend/listFolders.test.ts index e5195f33..003ad634 100644 --- a/tests/backend/listFolders.test.ts +++ b/tests/backend/listFolders.test.ts @@ -2,8 +2,8 @@ import { expect, test, vi } from "vitest"; import { listFolders } from "../../src/backend/listFolders"; import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedSession } from "../test-utils/gas-stubs"; -import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; +import { mockedSession } from "./test-utils/gas-stubs"; +import { mockedSafeDriveService } from "./test-utils/SafeDriveService-stub"; vi.mock("../../src/backend/utils/SafeDriveService"); diff --git a/tests/backend/listSharedDrives.test.ts b/tests/backend/listSharedDrives.test.ts index 00f297f4..09b64579 100644 --- a/tests/backend/listSharedDrives.test.ts +++ b/tests/backend/listSharedDrives.test.ts @@ -2,7 +2,7 @@ import { expect, test, vi } from "vitest"; import { listSharedDrives } from "../../src/backend/listSharedDrives"; import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "./test-utils/SafeDriveService-stub"; vi.mock("../../src/backend/utils/SafeDriveService"); diff --git a/tests/backend/move.test.ts b/tests/backend/move.test.ts index 597556e5..827bbdb0 100644 --- a/tests/backend/move.test.ts +++ b/tests/backend/move.test.ts @@ -5,7 +5,7 @@ import * as folderManagement from "../../src/backend/move/folderManagement"; import * as moveFolder from "../../src/backend/move/moveFolder"; import { MoveState_ } from "../../src/backend/utils/MoveState"; import { SafeDriveService_ } from "../../src/backend/utils/SafeDriveService"; -import { mockedMoveState } from "../test-utils/MoveState-stub"; +import { mockedMoveState } from "./test-utils/MoveState-stub"; vi.mock("../../src/backend/move/folderManagement"); vi.mock("../../src/backend/move/moveFolder"); diff --git a/tests/backend/move/copyFileComments.test.ts b/tests/backend/move/copyFileComments.test.ts index adcc8994..ea1ae56c 100644 --- a/tests/backend/move/copyFileComments.test.ts +++ b/tests/backend/move/copyFileComments.test.ts @@ -1,7 +1,7 @@ import { expect, test, vi } from "vitest"; import { copyFileComments_ } from "../../../src/backend/move/copyFileComments"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; test("copyFileComments works correctly", () => { interface ListCommentsOptions { diff --git a/tests/backend/move/folderManagement.test.ts b/tests/backend/move/folderManagement.test.ts index c169dd40..e48d78c0 100644 --- a/tests/backend/move/folderManagement.test.ts +++ b/tests/backend/move/folderManagement.test.ts @@ -6,7 +6,7 @@ import { listFilesInFolder_, listFoldersInFolder_, } from "../../../src/backend/move/folderManagement"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; test("listFilesInFolder works correctly", () => { interface ListFilesOptions { diff --git a/tests/backend/move/moveFile.test.ts b/tests/backend/move/moveFile.test.ts index a0ef6eb8..e33151ac 100644 --- a/tests/backend/move/moveFile.test.ts +++ b/tests/backend/move/moveFile.test.ts @@ -3,7 +3,7 @@ import { expect, test, vi } from "vitest"; import * as copyFileComments from "../../../src/backend/move/copyFileComments"; import { moveFile_ } from "../../../src/backend/move/moveFile"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; vi.mock("../../../src/backend/utils/MoveState"); vi.mock("../../../src/backend/move/copyFileComments"); diff --git a/tests/backend/move/moveFolder.test.ts b/tests/backend/move/moveFolder.test.ts index 3fa9bb6b..8826f54f 100644 --- a/tests/backend/move/moveFolder.test.ts +++ b/tests/backend/move/moveFolder.test.ts @@ -5,7 +5,7 @@ import * as moveFile from "../../../src/backend/move/moveFile"; import { moveFolder_ } from "../../../src/backend/move/moveFolder"; import * as resolveDestinationFolder from "../../../src/backend/move/resolveDestinationFolder"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; vi.mock("../../../src/backend/move/folderManagement"); vi.mock("../../../src/backend/move/moveFile"); diff --git a/tests/backend/move/resolveDestinationFolder.test.ts b/tests/backend/move/resolveDestinationFolder.test.ts index f5631104..5af4070e 100644 --- a/tests/backend/move/resolveDestinationFolder.test.ts +++ b/tests/backend/move/resolveDestinationFolder.test.ts @@ -3,7 +3,7 @@ import { expect, test, vi } from "vitest"; import * as folderManagement from "../../../src/backend/move/folderManagement"; import { resolveDestinationFolder_ } from "../../../src/backend/move/resolveDestinationFolder"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; vi.mock("../../../src/backend/utils/MoveState"); vi.mock("../../../src/backend/move/folderManagement"); diff --git a/tests/test-utils/DriveBackedValue-stub.ts b/tests/backend/test-utils/DriveBackedValue-stub.ts similarity index 65% rename from tests/test-utils/DriveBackedValue-stub.ts rename to tests/backend/test-utils/DriveBackedValue-stub.ts index ff4e6410..b1082a6a 100644 --- a/tests/test-utils/DriveBackedValue-stub.ts +++ b/tests/backend/test-utils/DriveBackedValue-stub.ts @@ -1,8 +1,8 @@ import { type MockedObject, vi } from "vitest"; -import type { DriveBackedValue_ } from "../../src/backend/utils/DriveBackedValue"; -import type { MoveContext } from "../../src/interfaces/MoveContext"; -import type { MoveError } from "../../src/interfaces/MoveError"; +import type { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; +import type { MoveContext } from "../../../src/interfaces/MoveContext"; +import type { MoveError } from "../../../src/interfaces/MoveError"; export function mockedDriveBackedValue(): MockedObject< DriveBackedValue_<{ diff --git a/tests/test-utils/MoveState-stub.ts b/tests/backend/test-utils/MoveState-stub.ts similarity index 85% rename from tests/test-utils/MoveState-stub.ts rename to tests/backend/test-utils/MoveState-stub.ts index a8fe7281..c3342f46 100644 --- a/tests/test-utils/MoveState-stub.ts +++ b/tests/backend/test-utils/MoveState-stub.ts @@ -1,6 +1,6 @@ import { type MockedObject, vi } from "vitest"; -import type { MoveState_ } from "../../src/backend/utils/MoveState"; +import type { MoveState_ } from "../../../src/backend/utils/MoveState"; export function mockedMoveState(): MockedObject { return { diff --git a/tests/test-utils/SafeDriveService-stub.ts b/tests/backend/test-utils/SafeDriveService-stub.ts similarity index 93% rename from tests/test-utils/SafeDriveService-stub.ts rename to tests/backend/test-utils/SafeDriveService-stub.ts index 5d74fcd2..ead7c0b0 100644 --- a/tests/test-utils/SafeDriveService-stub.ts +++ b/tests/backend/test-utils/SafeDriveService-stub.ts @@ -1,7 +1,7 @@ import { type MockedObject, vi } from "vitest"; -import type { DeepKeyof } from "../../src/backend/utils/DeepKeyof"; -import type { DeepPick } from "../../src/backend/utils/DeepPick"; +import type { DeepKeyof } from "../../../src/backend/utils/DeepKeyof"; +import type { DeepPick } from "../../../src/backend/utils/DeepPick"; import type { SafeComment, SafeCommentList, @@ -9,7 +9,7 @@ import type { SafeDriveService_, SafeFile, SafeFileList, -} from "../../src/backend/utils/SafeDriveService"; +} from "../../../src/backend/utils/SafeDriveService"; import { mockedRepliesCollection } from "./gas-stubs"; diff --git a/tests/test-utils/gas-stubs.ts b/tests/backend/test-utils/gas-stubs.ts similarity index 100% rename from tests/test-utils/gas-stubs.ts rename to tests/backend/test-utils/gas-stubs.ts diff --git a/tests/backend/utils/DriveBackedValue.test.ts b/tests/backend/utils/DriveBackedValue.test.ts index 8a7d60bd..1dd959ea 100644 --- a/tests/backend/utils/DriveBackedValue.test.ts +++ b/tests/backend/utils/DriveBackedValue.test.ts @@ -1,8 +1,8 @@ import { expect, test, vi } from "vitest"; import { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; -import { mockedUtilities } from "../../test-utils/gas-stubs"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedUtilities } from "../test-utils/gas-stubs"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; test("DriveBackedValue constructs correctly", () => { const key = "SAVE_KEY"; diff --git a/tests/backend/utils/MoveState.test.ts b/tests/backend/utils/MoveState.test.ts index 36ec5049..4d51aa77 100644 --- a/tests/backend/utils/MoveState.test.ts +++ b/tests/backend/utils/MoveState.test.ts @@ -2,8 +2,8 @@ import { expect, test, vi } from "vitest"; import { DriveBackedValue_ } from "../../../src/backend/utils/DriveBackedValue"; import { MoveState_ } from "../../../src/backend/utils/MoveState"; -import { mockedDriveBackedValue } from "../../test-utils/DriveBackedValue-stub"; -import { mockedSafeDriveService } from "../../test-utils/SafeDriveService-stub"; +import { mockedDriveBackedValue } from "../test-utils/DriveBackedValue-stub"; +import { mockedSafeDriveService } from "../test-utils/SafeDriveService-stub"; vi.mock("../../../src/backend/utils/DriveBackedValue"); diff --git a/tests/backend/utils/SafeDriveService.test.ts b/tests/backend/utils/SafeDriveService.test.ts index 93104eb7..02140872 100644 --- a/tests/backend/utils/SafeDriveService.test.ts +++ b/tests/backend/utils/SafeDriveService.test.ts @@ -7,7 +7,7 @@ import { mockedDrivesCollection, mockedFilesCollection, mockedRepliesCollection, -} from "../../test-utils/gas-stubs"; +} from "../test-utils/gas-stubs"; test("SafeDriveService constructs correctly", () => { global.Drive = { diff --git a/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts index 954cf5a0..fa06f991 100644 --- a/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeCommentsCollection.test.ts @@ -4,7 +4,7 @@ import { SafeCommentsCollection_ } from "../../../../src/backend/utils/SafeDrive import { mockedCommentsCollection, mockedDrive, -} from "../../../test-utils/gas-stubs"; +} from "../../test-utils/gas-stubs"; test("SafeCommentsCollection constructs correctly", () => { global.Drive = { diff --git a/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts index dbe8a5b1..048f6f42 100644 --- a/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeDrivesCollection.test.ts @@ -4,7 +4,7 @@ import { SafeDrivesCollection_ } from "../../../../src/backend/utils/SafeDriveSe import { mockedDrive, mockedDrivesCollection, -} from "../../../test-utils/gas-stubs"; +} from "../../test-utils/gas-stubs"; test("SafeDrivesCollection constructs correctly", () => { global.Drive = { diff --git a/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts b/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts index 37f86626..4c950c37 100644 --- a/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts +++ b/tests/backend/utils/SafeDriveService/SafeFilesCollection.test.ts @@ -1,10 +1,7 @@ import { expect, test, vi } from "vitest"; import { SafeFilesCollection_ } from "../../../../src/backend/utils/SafeDriveService/SafeFilesCollection"; -import { - mockedDrive, - mockedFilesCollection, -} from "../../../test-utils/gas-stubs"; +import { mockedDrive, mockedFilesCollection } from "../../test-utils/gas-stubs"; test("SafeFilesCollection constructs correctly", () => { global.Drive = { From c3a26a615379dfaa472c8883a7608fd659ff5c08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:49:30 +0100 Subject: [PATCH 20/23] Moved playwright tests to tests/ --- eslint.config.js | 4 ++-- playwright.config.ts | 2 +- {__tests__ => tests}/frontend/basic.spec.ts | 0 {__tests__ => tests}/frontend/configuration.spec.ts | 0 .../frontend/destination-selection-api-error.spec.ts | 0 .../destination-selection-invalid-parameter-error.spec.ts | 0 .../frontend/destination-selection-unhandled-error.spec.ts | 0 .../frontend/destination-selection-unknown-error.spec.ts | 0 {__tests__ => tests}/frontend/move-api-error.spec.ts | 0 .../frontend/move-folders-equal-error.spec.ts | 0 .../frontend/move-invalid-parameter-error.spec.ts | 0 .../frontend/move-repeat-after-timeout.spec.ts | 0 {__tests__ => tests}/frontend/move-unhandled-error.spec.ts | 0 {__tests__ => tests}/frontend/move-unknown-error.spec.ts | 0 {__tests__ => tests}/frontend/navigation.spec.ts | 0 {__tests__ => tests}/frontend/non-empty.spec.ts | 0 .../frontend/source-destination-selection.spec.ts | 0 .../frontend/source-selection-api-error.spec.ts | 0 .../frontend/source-selection-invalid-parameter-error.spec.ts | 0 .../frontend/source-selection-unhandled-error.spec.ts | 0 .../frontend/source-selection-unknown-error.spec.ts | 0 {__tests__ => tests}/frontend/success-with-errors.spec.ts | 0 {__tests__ => tests}/frontend/test-utils/stub-endpoints.ts | 0 23 files changed, 3 insertions(+), 3 deletions(-) rename {__tests__ => tests}/frontend/basic.spec.ts (100%) rename {__tests__ => tests}/frontend/configuration.spec.ts (100%) rename {__tests__ => tests}/frontend/destination-selection-api-error.spec.ts (100%) rename {__tests__ => tests}/frontend/destination-selection-invalid-parameter-error.spec.ts (100%) rename {__tests__ => tests}/frontend/destination-selection-unhandled-error.spec.ts (100%) rename {__tests__ => tests}/frontend/destination-selection-unknown-error.spec.ts (100%) rename {__tests__ => tests}/frontend/move-api-error.spec.ts (100%) rename {__tests__ => tests}/frontend/move-folders-equal-error.spec.ts (100%) rename {__tests__ => tests}/frontend/move-invalid-parameter-error.spec.ts (100%) rename {__tests__ => tests}/frontend/move-repeat-after-timeout.spec.ts (100%) rename {__tests__ => tests}/frontend/move-unhandled-error.spec.ts (100%) rename {__tests__ => tests}/frontend/move-unknown-error.spec.ts (100%) rename {__tests__ => tests}/frontend/navigation.spec.ts (100%) rename {__tests__ => tests}/frontend/non-empty.spec.ts (100%) rename {__tests__ => tests}/frontend/source-destination-selection.spec.ts (100%) rename {__tests__ => tests}/frontend/source-selection-api-error.spec.ts (100%) rename {__tests__ => tests}/frontend/source-selection-invalid-parameter-error.spec.ts (100%) rename {__tests__ => tests}/frontend/source-selection-unhandled-error.spec.ts (100%) rename {__tests__ => tests}/frontend/source-selection-unknown-error.spec.ts (100%) rename {__tests__ => tests}/frontend/success-with-errors.spec.ts (100%) rename {__tests__ => tests}/frontend/test-utils/stub-endpoints.ts (100%) diff --git a/eslint.config.js b/eslint.config.js index e318c39d..69636e00 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -218,7 +218,7 @@ export default tseslint.config( }, }, { - files: ["tests/**/*.ts"], + files: ["tests/backend/**/*.ts"], ...vitest.configs.recommended, rules: { ...vitest.configs.recommended.rules, @@ -308,7 +308,7 @@ export default tseslint.config( }, { ...playwright.configs["flat/recommended"], - files: ["__tests__/frontend/**/*.ts"], + files: ["tests/frontend/**/*.ts"], rules: { ...playwright.configs["flat/recommended"].rules, "playwright/no-commented-out-tests": "error", diff --git a/playwright.config.ts b/playwright.config.ts index 2a2c20ac..2a93f52a 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -19,7 +19,7 @@ export default defineConfig({ ], reporter: process.env.CI !== undefined ? "html" : "list", retries: process.env.CI !== undefined ? 2 : 0, - testDir: "./__tests__/frontend", + testDir: "./tests/frontend", use: { baseURL: "http://127.0.0.1:5173", trace: "on-first-retry", diff --git a/__tests__/frontend/basic.spec.ts b/tests/frontend/basic.spec.ts similarity index 100% rename from __tests__/frontend/basic.spec.ts rename to tests/frontend/basic.spec.ts diff --git a/__tests__/frontend/configuration.spec.ts b/tests/frontend/configuration.spec.ts similarity index 100% rename from __tests__/frontend/configuration.spec.ts rename to tests/frontend/configuration.spec.ts diff --git a/__tests__/frontend/destination-selection-api-error.spec.ts b/tests/frontend/destination-selection-api-error.spec.ts similarity index 100% rename from __tests__/frontend/destination-selection-api-error.spec.ts rename to tests/frontend/destination-selection-api-error.spec.ts diff --git a/__tests__/frontend/destination-selection-invalid-parameter-error.spec.ts b/tests/frontend/destination-selection-invalid-parameter-error.spec.ts similarity index 100% rename from __tests__/frontend/destination-selection-invalid-parameter-error.spec.ts rename to tests/frontend/destination-selection-invalid-parameter-error.spec.ts diff --git a/__tests__/frontend/destination-selection-unhandled-error.spec.ts b/tests/frontend/destination-selection-unhandled-error.spec.ts similarity index 100% rename from __tests__/frontend/destination-selection-unhandled-error.spec.ts rename to tests/frontend/destination-selection-unhandled-error.spec.ts diff --git a/__tests__/frontend/destination-selection-unknown-error.spec.ts b/tests/frontend/destination-selection-unknown-error.spec.ts similarity index 100% rename from __tests__/frontend/destination-selection-unknown-error.spec.ts rename to tests/frontend/destination-selection-unknown-error.spec.ts diff --git a/__tests__/frontend/move-api-error.spec.ts b/tests/frontend/move-api-error.spec.ts similarity index 100% rename from __tests__/frontend/move-api-error.spec.ts rename to tests/frontend/move-api-error.spec.ts diff --git a/__tests__/frontend/move-folders-equal-error.spec.ts b/tests/frontend/move-folders-equal-error.spec.ts similarity index 100% rename from __tests__/frontend/move-folders-equal-error.spec.ts rename to tests/frontend/move-folders-equal-error.spec.ts diff --git a/__tests__/frontend/move-invalid-parameter-error.spec.ts b/tests/frontend/move-invalid-parameter-error.spec.ts similarity index 100% rename from __tests__/frontend/move-invalid-parameter-error.spec.ts rename to tests/frontend/move-invalid-parameter-error.spec.ts diff --git a/__tests__/frontend/move-repeat-after-timeout.spec.ts b/tests/frontend/move-repeat-after-timeout.spec.ts similarity index 100% rename from __tests__/frontend/move-repeat-after-timeout.spec.ts rename to tests/frontend/move-repeat-after-timeout.spec.ts diff --git a/__tests__/frontend/move-unhandled-error.spec.ts b/tests/frontend/move-unhandled-error.spec.ts similarity index 100% rename from __tests__/frontend/move-unhandled-error.spec.ts rename to tests/frontend/move-unhandled-error.spec.ts diff --git a/__tests__/frontend/move-unknown-error.spec.ts b/tests/frontend/move-unknown-error.spec.ts similarity index 100% rename from __tests__/frontend/move-unknown-error.spec.ts rename to tests/frontend/move-unknown-error.spec.ts diff --git a/__tests__/frontend/navigation.spec.ts b/tests/frontend/navigation.spec.ts similarity index 100% rename from __tests__/frontend/navigation.spec.ts rename to tests/frontend/navigation.spec.ts diff --git a/__tests__/frontend/non-empty.spec.ts b/tests/frontend/non-empty.spec.ts similarity index 100% rename from __tests__/frontend/non-empty.spec.ts rename to tests/frontend/non-empty.spec.ts diff --git a/__tests__/frontend/source-destination-selection.spec.ts b/tests/frontend/source-destination-selection.spec.ts similarity index 100% rename from __tests__/frontend/source-destination-selection.spec.ts rename to tests/frontend/source-destination-selection.spec.ts diff --git a/__tests__/frontend/source-selection-api-error.spec.ts b/tests/frontend/source-selection-api-error.spec.ts similarity index 100% rename from __tests__/frontend/source-selection-api-error.spec.ts rename to tests/frontend/source-selection-api-error.spec.ts diff --git a/__tests__/frontend/source-selection-invalid-parameter-error.spec.ts b/tests/frontend/source-selection-invalid-parameter-error.spec.ts similarity index 100% rename from __tests__/frontend/source-selection-invalid-parameter-error.spec.ts rename to tests/frontend/source-selection-invalid-parameter-error.spec.ts diff --git a/__tests__/frontend/source-selection-unhandled-error.spec.ts b/tests/frontend/source-selection-unhandled-error.spec.ts similarity index 100% rename from __tests__/frontend/source-selection-unhandled-error.spec.ts rename to tests/frontend/source-selection-unhandled-error.spec.ts diff --git a/__tests__/frontend/source-selection-unknown-error.spec.ts b/tests/frontend/source-selection-unknown-error.spec.ts similarity index 100% rename from __tests__/frontend/source-selection-unknown-error.spec.ts rename to tests/frontend/source-selection-unknown-error.spec.ts diff --git a/__tests__/frontend/success-with-errors.spec.ts b/tests/frontend/success-with-errors.spec.ts similarity index 100% rename from __tests__/frontend/success-with-errors.spec.ts rename to tests/frontend/success-with-errors.spec.ts diff --git a/__tests__/frontend/test-utils/stub-endpoints.ts b/tests/frontend/test-utils/stub-endpoints.ts similarity index 100% rename from __tests__/frontend/test-utils/stub-endpoints.ts rename to tests/frontend/test-utils/stub-endpoints.ts From bf0b7722449e162d85e87e6b4d4a2b1761d960e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:51:48 +0100 Subject: [PATCH 21/23] Removed jest --- eslint.config.js | 40 - jest.config.js | 30 - package-lock.json | 2415 ++------------------------------------------- package.json | 9 +- 4 files changed, 93 insertions(+), 2401 deletions(-) delete mode 100644 jest.config.js diff --git a/eslint.config.js b/eslint.config.js index 69636e00..4e49f20e 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -2,7 +2,6 @@ import eslintComments from "@eslint-community/eslint-plugin-eslint-comments"; import commentsConfig from "@eslint-community/eslint-plugin-eslint-comments/configs"; import js from "@eslint/js"; import vitest from "@vitest/eslint-plugin"; -import jest from "eslint-plugin-jest"; import perfectionist from "eslint-plugin-perfectionist"; import playwright from "eslint-plugin-playwright"; import preferArrowFunctions from "eslint-plugin-prefer-arrow-functions"; @@ -32,7 +31,6 @@ export default tseslint.config( }, plugins: { "eslint-comments": eslintComments, - jest, "prefer-arrow-functions": preferArrowFunctions, }, rules: { @@ -268,44 +266,6 @@ export default tseslint.config( "vitest/valid-expect-in-promise": "error", }, }, - { - ...jest.configs["flat/recommended"], - ...jest.configs["flat/style"], - files: ["__tests__/**/*.test.ts", "__tests__/test-utils/gas-stubs.ts"], - rules: { - ...jest.configs["flat/recommended"].rules, - ...jest.configs["flat/style"].rules, - "jest/consistent-test-it": ["error", { withinDescribe: "test" }], - "jest/no-conditional-in-test": "error", - "jest/no-confusing-set-timeout": "error", - "jest/no-duplicate-hooks": "error", - "jest/no-test-return-statement": "error", - "jest/no-untyped-mock-factory": "error", - "jest/padding-around-all": "error", - "jest/prefer-called-with": "error", - "jest/prefer-comparison-matcher": "error", - "jest/prefer-each": "error", - "jest/prefer-equality-matcher": "error", - "jest/prefer-expect-assertions": [ - "error", - { - onlyFunctionsWithAsyncKeyword: true, - onlyFunctionsWithExpectInCallback: true, - onlyFunctionsWithExpectInLoop: true, - }, - ], - "jest/prefer-expect-resolves": "error", - "jest/prefer-hooks-in-order": "error", - "jest/prefer-hooks-on-top": "error", - "jest/prefer-jest-mocked": "error", - "jest/prefer-mock-promise-shorthand": "error", - "jest/prefer-spy-on": "error", - "jest/prefer-strict-equal": "error", - "jest/require-hook": "error", - "jest/require-to-throw-message": "error", - "jest/unbound-method": "error", - }, - }, { ...playwright.configs["flat/recommended"], files: ["tests/frontend/**/*.ts"], diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index da09b588..00000000 --- a/jest.config.js +++ /dev/null @@ -1,30 +0,0 @@ -/** @type {import('jest').Config} */ -export default { - collectCoverage: true, - collectCoverageFrom: [ - "src/**/*.svelte", - "src/**/*.ts", - "!src/**/*.d.ts", - "!src/backend/index.ts", - "!src/frontend/index.ts", - ], - coverageDirectory: "coverage", - coverageProvider: "babel", - projects: [ - { - displayName: "backend", - injectGlobals: false, - resetMocks: true, - testMatch: ["/__tests__/backend/**/*.test.ts"], - transform: { - "^.+\\.ts$": [ - "ts-jest", - { - // ts-jest configuration goes here - tsconfig: "test.backend.tsconfig.json", - }, - ], - }, - }, - ], -}; diff --git a/package-lock.json b/package-lock.json index dc63cfbf..fcf7cd26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,18 +25,15 @@ "@sveltejs/vite-plugin-svelte": "^3.1.2", "@types/google-apps-script": "^1.0.91", "@types/google.script.client-side": "^0.1.5", - "@types/jest": "^29.5.14", "@types/node": "^22.10.2", "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jest": "^28.9.0", "eslint-plugin-perfectionist": "^4.6.0", "eslint-plugin-playwright": "^2.1.0", "eslint-plugin-prefer-arrow-functions": "^3.4.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-svelte": "^2.46.1", - "jest": "^29.7.0", "npm-run-all": "^4.1.5", "nyc": "^17.1.0", "playwright": "^1.49.1", @@ -50,7 +47,6 @@ "svelte-eslint-parser": "^0.43.0", "svelte-i18n": "^4.0.1", "svelte-material-ui": "^7.0.0", - "ts-jest": "^29.2.5", "typescript": "^5.7.3", "typescript-eslint": "^8.19.1", "vite": "^5.4.11", @@ -230,16 +226,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", - "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-simple-access": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", @@ -408,245 +394,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", - "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", - "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.25.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", - "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/template": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", @@ -706,13 +453,6 @@ "node": ">=6.9.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -1618,399 +1358,107 @@ "node": ">=8" } }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=6.0.0" } }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6.0.0" } }, - "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==", + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", "dev": true, "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "node_modules/@material/animation": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-14.0.0.tgz", + "integrity": "sha512-VlYSfUaIj/BBVtRZI8Gv0VvzikFf+XgK0Zdgsok5c1v5DDnNz5tpB8mnGrveWz0rHbp1X4+CWLKrTwNmjrw3Xw==", "dev": true, "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "tslib": "^2.1.0" } }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "node_modules/@material/banner": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-14.0.0.tgz", + "integrity": "sha512-z0WPBVQxbQVcV1km4hFD40xBEeVWYtCzl2jrkHd8xXexP/fMvXkFU1UfwSWvY3jlWx//j4/Xd7VpnRdEXS4RLQ==", "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" + "@material/base": "^14.0.0", + "@material/button": "^14.0.0", + "@material/dom": "^14.0.0", + "@material/elevation": "^14.0.0", + "@material/feature-targeting": "^14.0.0", + "@material/ripple": "^14.0.0", + "@material/rtl": "^14.0.0", + "@material/shape": "^14.0.0", + "@material/theme": "^14.0.0", + "@material/tokens": "^14.0.0", + "@material/typography": "^14.0.0", + "tslib": "^2.1.0" } }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/@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/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/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/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@material/animation": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@material/animation/-/animation-14.0.0.tgz", - "integrity": "sha512-VlYSfUaIj/BBVtRZI8Gv0VvzikFf+XgK0Zdgsok5c1v5DDnNz5tpB8mnGrveWz0rHbp1X4+CWLKrTwNmjrw3Xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@material/banner": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@material/banner/-/banner-14.0.0.tgz", - "integrity": "sha512-z0WPBVQxbQVcV1km4hFD40xBEeVWYtCzl2jrkHd8xXexP/fMvXkFU1UfwSWvY3jlWx//j4/Xd7VpnRdEXS4RLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@material/base": "^14.0.0", - "@material/button": "^14.0.0", - "@material/dom": "^14.0.0", - "@material/elevation": "^14.0.0", - "@material/feature-targeting": "^14.0.0", - "@material/ripple": "^14.0.0", - "@material/rtl": "^14.0.0", - "@material/shape": "^14.0.0", - "@material/theme": "^14.0.0", - "@material/tokens": "^14.0.0", - "@material/typography": "^14.0.0", - "tslib": "^2.1.0" - } - }, - "node_modules/@material/base": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-14.0.0.tgz", - "integrity": "sha512-Ou7vS7n1H4Y10MUZyYAbt6H0t67c6urxoCgeVT7M38aQlaNUwFMODp7KT/myjYz2YULfhu3PtfSV3Sltgac9mA==", + "node_modules/@material/base": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-14.0.0.tgz", + "integrity": "sha512-Ou7vS7n1H4Y10MUZyYAbt6H0t67c6urxoCgeVT7M38aQlaNUwFMODp7KT/myjYz2YULfhu3PtfSV3Sltgac9mA==", "dev": true, "license": "MIT", "dependencies": { @@ -3402,13 +2850,6 @@ "win32" ] }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -3422,26 +2863,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, "node_modules/@smui-extra/accordion": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@smui-extra/accordion/-/accordion-7.0.0.tgz", @@ -4117,51 +3538,6 @@ "node": ">=10" } }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -4207,16 +3583,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -4224,43 +3590,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -4311,30 +3640,6 @@ "@types/node": "*" } }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.19.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.19.1.tgz", @@ -4929,13 +4234,6 @@ "node": ">=12" } }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "dev": true, - "license": "MIT" - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4962,143 +4260,17 @@ "node": ">= 0.4" } }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } + "license": "MIT" }, - "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": { - "@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/babel-plugin-istanbul/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/babel-plugin-istanbul/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/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, "funding": [ { @@ -5223,29 +4395,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -5283,7 +4432,9 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "peer": true }, "node_modules/cac": { "version": "6.7.14", @@ -5448,16 +4599,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -5500,29 +4641,6 @@ "fsevents": "~2.3.2" } }, - "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/cjs-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", - "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", - "dev": true, - "license": "MIT" - }, "node_modules/clean-stack": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", @@ -5635,17 +4753,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, "node_modules/code-red": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", @@ -5660,13 +4767,6 @@ "periscopic": "^3.1.0" } }, - "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==", - "dev": true, - "license": "MIT" - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5718,28 +4818,6 @@ "dev": true, "license": "MIT" }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5926,21 +5004,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, "node_modules/dedent-js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dedent-js/-/dedent-js-1.0.1.tgz", @@ -6093,26 +5156,6 @@ "node": ">=0.10" } }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/dotf": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/dotf/-/dotf-2.0.2.tgz", @@ -6144,22 +5187,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/electron-to-chromium": { "version": "1.5.35", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.35.tgz", @@ -6167,19 +5194,6 @@ "dev": true, "license": "ISC" }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -6557,31 +5571,6 @@ "eslint": ">=7.0.0" } }, - "node_modules/eslint-plugin-jest": { - "version": "28.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.9.0.tgz", - "integrity": "sha512-rLu1s1Wf96TgUUxSw6loVIkNtUjq1Re7A9QdCCHSohnvXEBAjuL420h0T/fMmkQlNsQP2GhQzEUpYHPfxBkvYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "engines": { - "node": "^16.10.0 || ^18.12.0 || >=20.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, "node_modules/eslint-plugin-perfectionist": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-perfectionist/-/eslint-plugin-perfectionist-4.6.0.tgz", @@ -6955,69 +5944,6 @@ "node": ">=6" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^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" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/expect-type": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", @@ -7120,16 +6046,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -7169,39 +6085,6 @@ "node": ">=16.0.0" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -7912,16 +6795,6 @@ "node": ">= 6" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -7988,26 +6861,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -8538,16 +7391,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -8989,729 +7832,33 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-cli/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==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-cli/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/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/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/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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-leak-detector": { - "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": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "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-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-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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/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-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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-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-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-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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-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": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "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==", + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "has-flag": "^4.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=10" + "node": "20 || >=22" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/js-tokens": { @@ -9850,32 +7997,12 @@ "json-buffer": "3.0.1" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/known-css-properties": { "version": "0.35.0", "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.35.0.tgz", "integrity": "sha512-a/RAk2BfKk+WFGhhOCAYqSiFLc34k8Mt/6NWRI4joER0EYUzXIcFivjjnoD3+XU1DggLn/tZc3DOAgke7l8a4A==", "dev": true }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -9984,13 +8111,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -10128,23 +8248,6 @@ "semver": "bin/semver.js" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -10181,13 +8284,6 @@ "node": ">= 0.10.0" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -10396,13 +8492,6 @@ "node": ">= 6.13.0" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, "node_modules/node-preload": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", @@ -10662,19 +8751,6 @@ "which": "bin/which" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/nyc": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-17.1.0.tgz", @@ -11567,16 +9643,6 @@ "node": ">=4" } }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -11906,34 +9972,6 @@ "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, - "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/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/process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -11947,20 +9985,6 @@ "node": ">=8" } }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/pump": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", @@ -11982,23 +10006,6 @@ "node": ">=6" } }, - "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/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -12055,13 +10062,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -12332,29 +10332,6 @@ "dev": true, "license": "MIT" }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -12364,16 +10341,6 @@ "node": ">=4" } }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -12835,23 +10802,6 @@ "dev": true, "license": "ISC" }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "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/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -12902,17 +10852,6 @@ "node": ">=0.10.0" } }, - "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/spawn-wrap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", @@ -13018,29 +10957,6 @@ "dev": true, "license": "BSD-3-Clause" }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -13088,20 +11004,6 @@ "dev": true, "license": "MIT" }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -13293,16 +11195,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -13787,13 +11679,6 @@ "node": ">=0.6.0" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -13836,55 +11721,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/ts-jest": { - "version": "29.2.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", - "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.6.3", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, "node_modules/ts2gas": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/ts2gas/-/ts2gas-4.2.0.tgz", @@ -13953,16 +11789,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", @@ -14206,21 +12032,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -14843,16 +12654,6 @@ } } }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -15073,30 +12874,6 @@ "dev": true, "license": "ISC" }, - "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/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -15104,16 +12881,6 @@ "dev": true, "license": "ISC" }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index ee293c4c..1d266970 100644 --- a/package.json +++ b/package.json @@ -30,18 +30,15 @@ "@sveltejs/vite-plugin-svelte": "^3.1.2", "@types/google-apps-script": "^1.0.91", "@types/google.script.client-side": "^0.1.5", - "@types/jest": "^29.5.14", "@types/node": "^22.10.2", "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jest": "^28.9.0", "eslint-plugin-perfectionist": "^4.6.0", "eslint-plugin-playwright": "^2.1.0", "eslint-plugin-prefer-arrow-functions": "^3.4.1", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-svelte": "^2.46.1", - "jest": "^29.7.0", "npm-run-all": "^4.1.5", "nyc": "^17.1.0", "playwright": "^1.49.1", @@ -55,7 +52,6 @@ "svelte-eslint-parser": "^0.43.0", "svelte-i18n": "^4.0.1", "svelte-material-ui": "^7.0.0", - "ts-jest": "^29.2.5", "typescript": "^5.7.3", "typescript-eslint": "^8.19.1", "vite": "^5.4.11", @@ -74,12 +70,11 @@ "lint:ts:typecheck:backend": "tsc --noEmit --project backend.tsconfig.json", "lint:ts:typecheck:frontend": "tsc --noEmit --project frontend.tsconfig.json", "lint:ts:typecheck": "run-p -c --aggregate-output lint:ts:typecheck:*", - "lint:ts:eslint": "eslint --color \"src/**/*.svelte\" \"src/**/*.ts\" \"tests/**/*.ts\" \"__tests__/**/*.ts\" \"*.config.{js,ts}\"", + "lint:ts:eslint": "eslint --color \"src/**/*.svelte\" \"src/**/*.ts\" \"tests/**/*.ts\" \"*.config.{js,ts}\"", "lint:ts": "run-p -c --aggregate-output lint:ts:*", "lint": "run-p -c --aggregate-output lint:*", "start": "vite --config frontend.vite.config.ts", - "test:backend": "jest", - "vitest:backend": "vitest --config backend.vite.config.ts", + "test:backend": "vitest --config backend.vite.config.ts", "test:frontend": "nyc --reporter=lcov playwright test", "test": "run-p -c test:*", "playwright-interactive": "playwright test --ui" From 2296337a980c0f547605cdc0ebe4d167e008fdc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 16:53:48 +0100 Subject: [PATCH 22/23] Separated test and test with coverage --- .github/workflows/CI.yml | 2 +- backend.vite.config.ts | 3 + package-lock.json | 181 +++++++++++++++++++++++++++++++++++++++ package.json | 3 +- 4 files changed, 187 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3be5a499..886028a3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -81,7 +81,7 @@ jobs: - name: "Run tests" run: | - npm run test:backend + npm run test-coverage:backend - name: "Upload coverage results" uses: codecov/codecov-action@v5.1.2 diff --git a/backend.vite.config.ts b/backend.vite.config.ts index 18649321..ddbf593f 100644 --- a/backend.vite.config.ts +++ b/backend.vite.config.ts @@ -13,6 +13,9 @@ export default defineConfig({ }, root: "src", test: { + coverage: { + include: ["src/backend/**/*.ts"], + }, dir: "tests/backend", mockReset: true, root: ".", diff --git a/package-lock.json b/package-lock.json index fcf7cd26..44b58df8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@types/google-apps-script": "^1.0.91", "@types/google.script.client-side": "^0.1.5", "@types/node": "^22.10.2", + "@vitest/coverage-v8": "^2.1.8", "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", @@ -453,6 +454,13 @@ "node": ">=6.9.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -2556,6 +2564,17 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -3836,6 +3855,156 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vitest/coverage-v8": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-2.1.8.tgz", + "integrity": "sha512-2Y7BPlKH18mAZYAW1tYByudlCYrQyl5RGvnnDYJKW5tCiO5qg3KSAy3XAxcxKz900a0ZXxWtKrMuZLe3lKBpJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.7", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.1.7", + "magic-string": "^0.30.12", + "magicast": "^0.3.5", + "std-env": "^3.8.0", + "test-exclude": "^7.0.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "2.1.8", + "vitest": "2.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/coverage-v8/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitest/coverage-v8/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vitest/coverage-v8/node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@vitest/eslint-plugin": { "version": "1.1.25", "resolved": "https://registry.npmjs.org/@vitest/eslint-plugin/-/eslint-plugin-1.1.25.tgz", @@ -8222,6 +8391,18 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", diff --git a/package.json b/package.json index 1d266970..f4c491d7 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "@types/google-apps-script": "^1.0.91", "@types/google.script.client-side": "^0.1.5", "@types/node": "^22.10.2", + "@vitest/coverage-v8": "^2.1.8", "@vitest/eslint-plugin": "^1.1.25", "eslint": "^9.17.0", "eslint-config-prettier": "^9.1.0", @@ -76,7 +77,7 @@ "start": "vite --config frontend.vite.config.ts", "test:backend": "vitest --config backend.vite.config.ts", "test:frontend": "nyc --reporter=lcov playwright test", - "test": "run-p -c test:*", + "test-coverage:backend": "vitest run --coverage --config backend.vite.config.ts", "playwright-interactive": "playwright test --ui" } } From 57297f6619ac12d216aa8d81c8c2615b0260def9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20D=C4=9Bdi=C4=8D?= Date: Wed, 15 Jan 2025 19:14:29 +0100 Subject: [PATCH 23/23] Eslint suppressions --- tests/backend/test-utils/DriveBackedValue-stub.ts | 1 + tests/backend/test-utils/MoveState-stub.ts | 1 + tests/backend/test-utils/SafeDriveService-stub.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/tests/backend/test-utils/DriveBackedValue-stub.ts b/tests/backend/test-utils/DriveBackedValue-stub.ts index b1082a6a..1b9f8828 100644 --- a/tests/backend/test-utils/DriveBackedValue-stub.ts +++ b/tests/backend/test-utils/DriveBackedValue-stub.ts @@ -10,6 +10,7 @@ export function mockedDriveBackedValue(): MockedObject< pathsToProcess: Array; }> > { + // eslint-disable-next-line vitest/prefer-vi-mocked -- Acceptable as return value return { deleteValue: vi.fn(), loadValue: vi.fn(), diff --git a/tests/backend/test-utils/MoveState-stub.ts b/tests/backend/test-utils/MoveState-stub.ts index c3342f46..541defd3 100644 --- a/tests/backend/test-utils/MoveState-stub.ts +++ b/tests/backend/test-utils/MoveState-stub.ts @@ -3,6 +3,7 @@ import { type MockedObject, vi } from "vitest"; import type { MoveState_ } from "../../../src/backend/utils/MoveState"; export function mockedMoveState(): MockedObject { + // eslint-disable-next-line vitest/prefer-vi-mocked -- Acceptable as return value return { addPath: vi.fn(), destroyState: vi.fn(), diff --git a/tests/backend/test-utils/SafeDriveService-stub.ts b/tests/backend/test-utils/SafeDriveService-stub.ts index ead7c0b0..522d5873 100644 --- a/tests/backend/test-utils/SafeDriveService-stub.ts +++ b/tests/backend/test-utils/SafeDriveService-stub.ts @@ -17,6 +17,7 @@ export function mockedSafeDriveService< // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- No other way to pass F to vi.fn() F extends DeepKeyof, >(): MockedObject { + // eslint-disable-next-line vitest/prefer-vi-mocked -- Acceptable as return value return { Comments: { insert: