diff --git a/.prettierrc.json b/.oxfmtrc.json similarity index 64% rename from .prettierrc.json rename to .oxfmtrc.json index 54211b89..0eca6fbc 100644 --- a/.prettierrc.json +++ b/.oxfmtrc.json @@ -1,22 +1,15 @@ { + "$schema": "./node_modules/oxfmt/configuration_schema.json", "printWidth": 80, "tabWidth": 4, "semi": true, "singleQuote": true, - "quoteProps": "as-needed", "trailingComma": "none", "bracketSpacing": true, "arrowParens": "always", - "endOfLine": "auto", "overrides": [ { - "files": "*.js", - "options": { - "parser": "meriyah" - } - }, - { - "files": "*.vue", + "files": ["*.vue"], "options": { "printWidth": 120, "bracketSameLine": true, diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 6c600fb1..53754fa8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,8 +3,7 @@ "vue.volar", "lokalise.i18n-ally", "dbaeumer.vscode-eslint", - "esbenp.prettier-vscode", - "lllllllqw.jsdoc", + "oxc.oxc-vscode", "bradlc.vscode-tailwindcss" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index f7e0adcd..724a9607 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,8 @@ "i18n-ally.keystyle": "nested", "i18n-ally.sourceLanguage": "en", "i18n-ally.indent": 4, - "editor.defaultFormatter": "esbenp.prettier-vscode", + "oxc.fmt.configPath": ".oxfmtrc.json", + "editor.defaultFormatter": "oxc.oxc-vscode", "editor.formatOnSave": true, "omnisharp.enableRoslynAnalyzers": true, "omnisharp.useModernNet": false, diff --git a/eslint.config.mjs b/eslint.config.mjs index 9dd78c76..75b34495 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,7 +1,6 @@ import { defineConfig } from 'eslint/config'; import { jsdoc } from 'eslint-plugin-jsdoc'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; import globals from 'globals'; import js from '@eslint/js'; import pluginVue from 'eslint-plugin-vue'; @@ -108,7 +107,7 @@ export default defineConfig([ 'jsdoc/require-returns-description': 'off', 'jsdoc/reject-function-type': 'off' } - }), + }) // { // ignores: [ // '**/__tests__/**', @@ -127,12 +126,4 @@ export default defineConfig([ // 'pretty-import/sort-import-names': 'warn' // } // }, - { - ...eslintPluginPrettierRecommended, - ignores: [ - '**/__tests__/**', - '**/*.spec.{js,mjs,cjs,vue}', - '**/*.test.{js,mjs,cjs,vue}' - ] - } ]); diff --git a/package-lock.json b/package-lock.json index f08de7d8..4a50659e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,9 +46,7 @@ "electron-builder": "^26.8.1", "embla-carousel-vue": "^8.6.0", "eslint": "^9.39.4", - "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsdoc": "^62.8.0", - "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-vue": "^9.33.0", "globals": "^17.4.0", "graphology": "^0.26.0", @@ -59,8 +57,8 @@ "lightningcss": "^1.32.0", "lucide-vue-next": "^0.562.0", "noty": "^3.2.0-beta-deprecated", + "oxfmt": "^0.40.0", "pinia": "^3.0.4", - "prettier": "^3.8.1", "reka-ui": "^2.9.1", "remixicon": "^4.9.1", "sigma": "^3.0.2", @@ -2243,6 +2241,353 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.40.0.tgz", + "integrity": "sha512-S6zd5r1w/HmqR8t0CTnGjFTBLDq2QKORPwriCHxo4xFNuhmOTABGjPaNvCJJVnrKBLsohOeiDX3YqQfJPF+FXw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.40.0.tgz", + "integrity": "sha512-/mbS9UUP/5Vbl2D6osIdcYiP0oie63LKMoTyGj5hyMCK/SFkl3EhtyRAfdjPvuvHC0SXdW6ePaTKkBSq1SNcIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.40.0.tgz", + "integrity": "sha512-wRt8fRdfLiEhnRMBonlIbKrJWixoEmn6KCjKE9PElnrSDSXETGZfPb8ee+nQNTobXkCVvVLytp2o0obAsxl78Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.40.0.tgz", + "integrity": "sha512-fzowhqbOE/NRy+AE5ob0+Y4X243WbWzDb00W+pKwD7d9tOqsAFbtWUwIyqqCoCLxj791m2xXIEeLH/3uz7zCCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.40.0.tgz", + "integrity": "sha512-agZ9ITaqdBjcerRRFEHB8s0OyVcQW8F9ZxsszjxzeSthQ4fcN2MuOtQFWec1ed8/lDa50jSLHVE2/xPmTgtCfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.40.0.tgz", + "integrity": "sha512-ZM2oQ47p28TP1DVIp7HL1QoMUgqlBFHey0ksHct7tMXoU5BqjNvPWw7888azzMt25lnyPODVuye1wvNbvVUFOA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.40.0.tgz", + "integrity": "sha512-RBFPAxRAIsMisKM47Oe6Lwdv6agZYLz02CUhVCD1sOv5ajAcRMrnwCFBPWwGXpazToW2mjnZxFos8TuFjTU15A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.40.0.tgz", + "integrity": "sha512-Nb2XbQ+wV3W2jSIihXdPj7k83eOxeSgYP3N/SRXvQ6ZYPIk6Q86qEh5Gl/7OitX3bQoQrESqm1yMLvZV8/J7dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.40.0.tgz", + "integrity": "sha512-tGmWhLD/0YMotCdfezlT6tC/MJG/wKpo4vnQ3Cq+4eBk/BwNv7EmkD0VkD5F/dYkT3b8FNU01X2e8vvJuWoM1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.40.0.tgz", + "integrity": "sha512-rVbFyM3e7YhkVnp0IVYjaSHfrBWcTRWb60LEcdNAJcE2mbhTpbqKufx0FrhWfoxOrW/+7UJonAOShoFFLigDqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.40.0.tgz", + "integrity": "sha512-3ZqBw14JtWeEoLiioJcXSJz8RQyPE+3jLARnYM1HdPzZG4vk+Ua8CUupt2+d+vSAvMyaQBTN2dZK+kbBS/j5mA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.40.0.tgz", + "integrity": "sha512-JJ4PPSdcbGBjPvb+O7xYm2FmAsKCyuEMYhqatBAHMp/6TA6rVlf9Z/sYPa4/3Bommb+8nndm15SPFRHEPU5qFA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.40.0.tgz", + "integrity": "sha512-Kp0zNJoX9Ik77wUya2tpBY3W9f40VUoMQLWVaob5SgCrblH/t2xr/9B2bWHfs0WCefuGmqXcB+t0Lq77sbBmZw==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.40.0.tgz", + "integrity": "sha512-7YTCNzleWTaQTqNGUNQ66qVjpoV6DjbCOea+RnpMBly2bpzrI/uu7Rr+2zcgRfNxyjXaFTVQKaRKjqVdeUfeVA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.40.0.tgz", + "integrity": "sha512-hWnSzJ0oegeOwfOEeejYXfBqmnRGHusgtHfCPzmvJvHTwy1s3Neo59UKc1CmpE3zxvrCzJoVHos0rr97GHMNPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.40.0.tgz", + "integrity": "sha512-28sJC1lR4qtBJGzSRRbPnSW3GxU2+4YyQFE6rCmsUYqZ5XYH8jg0/w+CvEzQ8TuAQz5zLkcA25nFQGwoU0PT3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.40.0.tgz", + "integrity": "sha512-cDkRnyT0dqwF5oIX1Cv59HKCeZQFbWWdUpXa3uvnHFT2iwYSSZspkhgjXjU6iDp5pFPaAEAe9FIbMoTgkTmKPg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.40.0.tgz", + "integrity": "sha512-7rPemBJjqm5Gkv6ZRCPvK8lE6AqQ/2z31DRdWazyx2ZvaSgL7QGofHXHNouRpPvNsT9yxRNQJgigsWkc+0qg4w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.40.0.tgz", + "integrity": "sha512-/Zmj0yTYSvmha6TG1QnoLqVT7ZMRDqXvFXXBQpIjteEwx9qvUYMBH2xbiOFhDeMUJkGwC3D6fdKsFtaqUvkwNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@pinia/testing": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@pinia/testing/-/testing-1.0.3.tgz", @@ -2267,19 +2612,6 @@ "node": ">=14" } }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", - "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, "node_modules/@preact/signals-core": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.13.0.tgz", @@ -6658,22 +6990,6 @@ } } }, - "node_modules/eslint-config-prettier": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", - "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, "node_modules/eslint-plugin-jsdoc": { "version": "62.8.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-62.8.0.tgz", @@ -6734,37 +7050,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.5.tgz", - "integrity": "sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, "node_modules/eslint-plugin-vue": { "version": "9.33.0", "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", @@ -6996,13 +7281,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -9465,6 +9743,46 @@ "node": ">=8" } }, + "node_modules/oxfmt": { + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.40.0.tgz", + "integrity": "sha512-g0C3I7xUj4b4DcagevM9kgH6+pUHytikxUcn3/VUkvzTNaaXBeyZqb7IBsHwojeXm4mTBEC/aBjBTMVUkZwWUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinypool": "2.1.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.40.0", + "@oxfmt/binding-android-arm64": "0.40.0", + "@oxfmt/binding-darwin-arm64": "0.40.0", + "@oxfmt/binding-darwin-x64": "0.40.0", + "@oxfmt/binding-freebsd-x64": "0.40.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.40.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.40.0", + "@oxfmt/binding-linux-arm64-gnu": "0.40.0", + "@oxfmt/binding-linux-arm64-musl": "0.40.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.40.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.40.0", + "@oxfmt/binding-linux-riscv64-musl": "0.40.0", + "@oxfmt/binding-linux-s390x-gnu": "0.40.0", + "@oxfmt/binding-linux-x64-gnu": "0.40.0", + "@oxfmt/binding-linux-x64-musl": "0.40.0", + "@oxfmt/binding-openharmony-arm64": "0.40.0", + "@oxfmt/binding-win32-arm64-msvc": "0.40.0", + "@oxfmt/binding-win32-ia32-msvc": "0.40.0", + "@oxfmt/binding-win32-x64-msvc": "0.40.0" + } + }, "node_modules/p-cancelable": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", @@ -9821,35 +10139,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.1.tgz", - "integrity": "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/proc-log": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", @@ -10809,22 +11098,6 @@ "dev": true, "license": "MIT" }, - "node_modules/synckit": { - "version": "0.11.12", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", - "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, "node_modules/tailwind-merge": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", @@ -11033,6 +11306,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinypool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz", + "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.0.0 || >=22.0.0" + } + }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", diff --git a/package.json b/package.json index 4de50af1..fc4a9628 100644 --- a/package.json +++ b/package.json @@ -65,9 +65,7 @@ "electron-builder": "^26.8.1", "embla-carousel-vue": "^8.6.0", "eslint": "^9.39.4", - "eslint-config-prettier": "^10.1.8", "eslint-plugin-jsdoc": "^62.8.0", - "eslint-plugin-prettier": "^5.5.5", "eslint-plugin-vue": "^9.33.0", "globals": "^17.4.0", "graphology": "^0.26.0", @@ -78,8 +76,8 @@ "lightningcss": "^1.32.0", "lucide-vue-next": "^0.562.0", "noty": "^3.2.0-beta-deprecated", + "oxfmt": "^0.40.0", "pinia": "^3.0.4", - "prettier": "^3.8.1", "reka-ui": "^2.9.1", "remixicon": "^4.9.1", "sigma": "^3.0.2", diff --git a/src-electron/offscreen-preload.js b/src-electron/offscreen-preload.js index 1417a98f..e8559086 100644 --- a/src-electron/offscreen-preload.js +++ b/src-electron/offscreen-preload.js @@ -1,5 +1,6 @@ const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('electronAPI', { - onUpdateImage: (callback) => ipcRenderer.on('update-image', (event, base64) => callback(base64)) -}); \ No newline at end of file + onUpdateImage: (callback) => + ipcRenderer.on('update-image', (event, base64) => callback(base64)) +}); diff --git a/src/api/__tests__/entityQuerySync.test.js b/src/api/__tests__/entityQuerySync.test.js index 3d0fbe99..668c5850 100644 --- a/src/api/__tests__/entityQuerySync.test.js +++ b/src/api/__tests__/entityQuerySync.test.js @@ -3,7 +3,10 @@ import { beforeEach, describe, expect, test, vi } from 'vitest'; const mockRequest = vi.fn(); const mockPatchAndRefetchActiveQuery = vi.fn(() => Promise.resolve()); -const mockApplyCurrentUser = vi.fn((json) => ({ id: json.id || 'usr_me', ...json })); +const mockApplyCurrentUser = vi.fn((json) => ({ + id: json.id || 'usr_me', + ...json +})); const mockApplyUser = vi.fn((json) => ({ ...json })); const mockApplyWorld = vi.fn((json) => ({ ...json })); @@ -91,5 +94,4 @@ describe('entity mutation query sync', () => { }) ); }); - }); diff --git a/src/api/__tests__/favoriteQuerySync.test.js b/src/api/__tests__/favoriteQuerySync.test.js index 2906635f..cb2772d4 100644 --- a/src/api/__tests__/favoriteQuerySync.test.js +++ b/src/api/__tests__/favoriteQuerySync.test.js @@ -19,8 +19,7 @@ vi.mock('../../stores', () => ({ vi.mock('../../coordinators/favoriteCoordinator', () => ({ handleFavoriteAdd: (...args) => mockHandleFavoriteAdd(...args), handleFavoriteDelete: (...args) => mockHandleFavoriteDelete(...args), - handleFavoriteGroupClear: (...args) => - mockHandleFavoriteGroupClear(...args) + handleFavoriteGroupClear: (...args) => mockHandleFavoriteGroupClear(...args) })); vi.mock('../../queries', () => ({ @@ -39,7 +38,10 @@ describe('favorite query sync', () => { test('favorite mutations invalidate active favorite queries', async () => { mockRequest.mockResolvedValue({ ok: true }); - await favoriteRequest.addFavorite({ type: 'world', favoriteId: 'wrld_1' }); + await favoriteRequest.addFavorite({ + type: 'world', + favoriteId: 'wrld_1' + }); await favoriteRequest.deleteFavorite({ objectId: 'fav_1' }); await favoriteRequest.saveFavoriteGroup({ type: 'world', diff --git a/src/api/__tests__/mediaQuerySync.test.js b/src/api/__tests__/mediaQuerySync.test.js index f2dbb87c..970e7ce6 100644 --- a/src/api/__tests__/mediaQuerySync.test.js +++ b/src/api/__tests__/mediaQuerySync.test.js @@ -24,7 +24,12 @@ vi.mock('../../queries', () => ({ prints: (params) => ['gallery', 'prints', params], print: (printId) => ['gallery', 'print', printId], inventoryItems: (params) => ['inventory', 'items', params], - userInventoryItem: (params) => ['inventory', 'item', params.userId, params.inventoryId], + userInventoryItem: (params) => [ + 'inventory', + 'item', + params.userId, + params.inventoryId + ], file: (fileId) => ['file', fileId] } })); diff --git a/src/api/__tests__/queryRequest.test.js b/src/api/__tests__/queryRequest.test.js index b59616a6..715f8b97 100644 --- a/src/api/__tests__/queryRequest.test.js +++ b/src/api/__tests__/queryRequest.test.js @@ -10,28 +10,138 @@ vi.mock('../../queries', () => ({ invalidateQueries: vi.fn().mockResolvedValue(undefined) }, entityQueryPolicies: { - user: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false }, - worldCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - groupCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - groupCalendarCollection: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, - groupFollowingCalendarCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - groupFeaturedCalendarCollection: { staleTime: 300000, gcTime: 900000, retry: 1, refetchOnWindowFocus: false }, - groupCalendarEvent: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, - avatar: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - avatarCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - avatarGallery: { staleTime: 30000, gcTime: 120000, retry: 1, refetchOnWindowFocus: false }, - world: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - group: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - friendList: { staleTime: 20000, gcTime: 90000, retry: 1, refetchOnWindowFocus: false }, - favoriteCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - galleryCollection: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - inventoryCollection: { staleTime: 20000, gcTime: 120000, retry: 1, refetchOnWindowFocus: false }, - inventoryObject: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false }, - fileAnalysis: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, - worldPersistData: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, - mutualCounts: { staleTime: 120000, gcTime: 600000, retry: 1, refetchOnWindowFocus: false }, - visits: { staleTime: 300000, gcTime: 900000, retry: 1, refetchOnWindowFocus: false }, - fileObject: { staleTime: 60000, gcTime: 300000, retry: 1, refetchOnWindowFocus: false } + user: { + staleTime: 20000, + gcTime: 90000, + retry: 1, + refetchOnWindowFocus: false + }, + worldCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + groupCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + groupCalendarCollection: { + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }, + groupFollowingCalendarCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + groupFeaturedCalendarCollection: { + staleTime: 300000, + gcTime: 900000, + retry: 1, + refetchOnWindowFocus: false + }, + groupCalendarEvent: { + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }, + avatar: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + avatarCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + avatarGallery: { + staleTime: 30000, + gcTime: 120000, + retry: 1, + refetchOnWindowFocus: false + }, + world: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + group: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + friendList: { + staleTime: 20000, + gcTime: 90000, + retry: 1, + refetchOnWindowFocus: false + }, + favoriteCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + galleryCollection: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + inventoryCollection: { + staleTime: 20000, + gcTime: 120000, + retry: 1, + refetchOnWindowFocus: false + }, + inventoryObject: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + }, + fileAnalysis: { + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }, + worldPersistData: { + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }, + mutualCounts: { + staleTime: 120000, + gcTime: 600000, + retry: 1, + refetchOnWindowFocus: false + }, + visits: { + staleTime: 300000, + gcTime: 900000, + retry: 1, + refetchOnWindowFocus: false + }, + fileObject: { + staleTime: 60000, + gcTime: 300000, + retry: 1, + refetchOnWindowFocus: false + } }, fetchWithEntityPolicy: (...args) => mockFetchWithEntityPolicy(...args), queryKeys: { @@ -40,16 +150,46 @@ vi.mock('../../queries', () => ({ worldsByUser: (params) => ['worlds', 'user', params.userId, params], groupCalendar: (groupId) => ['group', groupId, 'calendar'], groupCalendars: (params) => ['group', 'calendar', params], - followingGroupCalendars: (params) => ['group', 'calendar', 'following', params], - featuredGroupCalendars: (params) => ['group', 'calendar', 'featured', params], + followingGroupCalendars: (params) => [ + 'group', + 'calendar', + 'following', + params + ], + featuredGroupCalendars: (params) => [ + 'group', + 'calendar', + 'featured', + params + ], avatar: (avatarId) => ['avatar', avatarId], world: (worldId) => ['world', worldId], - group: (groupId, includeRoles) => ['group', groupId, Boolean(includeRoles)], + group: (groupId, includeRoles) => [ + 'group', + groupId, + Boolean(includeRoles) + ], groupPosts: (params) => ['group', params.groupId, 'posts', params], - groupMember: (params) => ['group', params.groupId, 'member', params.userId], + groupMember: (params) => [ + 'group', + params.groupId, + 'member', + params.userId + ], groupMembers: (params) => ['group', params.groupId, 'members', params], - groupGallery: (params) => ['group', params.groupId, 'gallery', params.galleryId, params], - groupCalendarEvent: (params) => ['group', params.groupId, 'calendarEvent', params.eventId], + groupGallery: (params) => [ + 'group', + params.groupId, + 'gallery', + params.galleryId, + params + ], + groupCalendarEvent: (params) => [ + 'group', + params.groupId, + 'calendarEvent', + params.eventId + ], avatarGallery: (avatarId) => ['avatar', avatarId, 'gallery'], friends: (params) => ['friends', params], favoriteLimits: () => ['favorite', 'limits'], @@ -61,10 +201,24 @@ vi.mock('../../queries', () => ({ prints: (params) => ['gallery', 'prints', params], print: (printId) => ['gallery', 'print', printId], inventoryItem: (inventoryId) => ['inventory', 'item', inventoryId], - userInventoryItem: (params) => ['inventory', 'item', params.userId, params.inventoryId], + userInventoryItem: (params) => [ + 'inventory', + 'item', + params.userId, + params.inventoryId + ], inventoryItems: (params) => ['inventory', 'items', params], - inventoryTemplate: (inventoryTemplateId) => ['inventory', 'template', inventoryTemplateId], - fileAnalysis: (params) => ['analysis', params.fileId, Number(params.version), String(params.variant || '')], + inventoryTemplate: (inventoryTemplateId) => [ + 'inventory', + 'template', + inventoryTemplateId + ], + fileAnalysis: (params) => [ + 'analysis', + params.fileId, + Number(params.version), + String(params.variant || '') + ], worldPersistData: (worldId) => ['world', worldId, 'persistData'], mutualCounts: (userId) => ['user', userId, 'mutualCounts'], visits: () => ['visits'], @@ -102,7 +256,11 @@ vi.mock('../group', () => ({ })); vi.mock('../avatar', () => ({ - default: { getAvatar: vi.fn(), getAvatarGallery: vi.fn(), getAvatars: vi.fn() } + default: { + getAvatar: vi.fn(), + getAvatarGallery: vi.fn(), + getAvatars: vi.fn() + } })); vi.mock('../friend', () => ({ default: { getFriends: vi.fn() } })); vi.mock('../favorite', () => ({ diff --git a/src/components/Location.vue b/src/components/Location.vue index af899540..da87d5d5 100644 --- a/src/components/Location.vue +++ b/src/components/Location.vue @@ -43,12 +43,7 @@ resolveRegion, translateAccessType } from '../shared/utils'; - import { - useAppearanceSettingsStore, - useInstanceStore, - useSearchStore, - useWorldStore - } from '../stores'; + import { useAppearanceSettingsStore, useInstanceStore, useSearchStore, useWorldStore } from '../stores'; import { showGroupDialog } from '../coordinators/groupCoordinator'; import { showWorldDialog } from '../coordinators/worldCoordinator'; import { Spinner } from './ui/spinner'; @@ -104,9 +99,7 @@ } ]); const tooltipContent = computed(() => `${t('dialog.new_instance.instance_id')}: #${instanceName.value}`); - const tooltipDisabled = computed( - () => props.disableTooltip || !instanceName.value || showInstanceIdInLocation.value - ); + const tooltipDisabled = computed(() => props.disableTooltip || !instanceName.value || showInstanceIdInLocation.value); const closedTooltip = computed(() => t('dialog.user.info.instance_closed')); let isDisposed = false; diff --git a/src/components/__tests__/AvatarInfo.test.js b/src/components/__tests__/AvatarInfo.test.js index 04a4e607..c4f48f07 100644 --- a/src/components/__tests__/AvatarInfo.test.js +++ b/src/components/__tests__/AvatarInfo.test.js @@ -216,7 +216,9 @@ describe('AvatarInfo.vue', () => { test('does not call showAvatarAuthorDialog when no imageurl', async () => { const wrapper = mountAvatarInfo({}); await wrapper.trigger('click'); - expect(avatarCoordinatorModule.showAvatarAuthorDialog).not.toHaveBeenCalled(); + expect( + avatarCoordinatorModule.showAvatarAuthorDialog + ).not.toHaveBeenCalled(); }); }); }); diff --git a/src/components/__tests__/BackToTop.test.js b/src/components/__tests__/BackToTop.test.js index 5f88b058..1aa16056 100644 --- a/src/components/__tests__/BackToTop.test.js +++ b/src/components/__tests__/BackToTop.test.js @@ -11,7 +11,8 @@ vi.mock('@/components/ui/tooltip', () => ({ vi.mock('@/components/ui/button', () => ({ Button: { emits: ['click'], - template: '' + template: + '' } })); @@ -58,7 +59,10 @@ describe('BackToTop.vue', () => { await btn.trigger('click'); - expect(window.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' }); + expect(window.scrollTo).toHaveBeenCalledWith({ + top: 0, + behavior: 'smooth' + }); }); it('uses virtualizer scrollToIndex when provided', async () => { @@ -78,7 +82,10 @@ describe('BackToTop.vue', () => { const btn = wrapper.get('[data-testid="back-btn"]'); await btn.trigger('click'); - expect(scrollToIndex).toHaveBeenCalledWith(0, { align: 'start', behavior: 'auto' }); + expect(scrollToIndex).toHaveBeenCalledWith(0, { + align: 'start', + behavior: 'auto' + }); expect(window.scrollTo).not.toHaveBeenCalled(); }); @@ -103,6 +110,9 @@ describe('BackToTop.vue', () => { const btn = wrapper.get('[data-testid="back-btn"]'); await btn.trigger('click'); - expect(target.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'auto' }); + expect(target.scrollTo).toHaveBeenCalledWith({ + top: 0, + behavior: 'auto' + }); }); }); diff --git a/src/components/__tests__/CountdownTimer.test.js b/src/components/__tests__/CountdownTimer.test.js index d759d215..9628e574 100644 --- a/src/components/__tests__/CountdownTimer.test.js +++ b/src/components/__tests__/CountdownTimer.test.js @@ -24,7 +24,9 @@ describe('CountdownTimer.vue', () => { mocks.setInterval.mockClear(); mocks.clearInterval.mockClear(); mocks.timeToText.mockClear(); - vi.spyOn(Date, 'now').mockReturnValue(new Date('2026-01-01T00:00:00.000Z').getTime()); + vi.spyOn(Date, 'now').mockReturnValue( + new Date('2026-01-01T00:00:00.000Z').getTime() + ); }); afterEach(() => { @@ -55,7 +57,10 @@ describe('CountdownTimer.vue', () => { expect(wrapper.text()).toBe('-'); - await wrapper.setProps({ datetime: '2025-12-31T23:59:30.000Z', hours: 0 }); + await wrapper.setProps({ + datetime: '2025-12-31T23:59:30.000Z', + hours: 0 + }); await nextTick(); expect(wrapper.text()).toBe('-'); }); diff --git a/src/components/__tests__/DeprecationAlert.test.js b/src/components/__tests__/DeprecationAlert.test.js index ff78ba82..0702de43 100644 --- a/src/components/__tests__/DeprecationAlert.test.js +++ b/src/components/__tests__/DeprecationAlert.test.js @@ -22,7 +22,8 @@ describe('DeprecationAlert.vue', () => { global: { stubs: { i18nT: { - template: '' + template: + '' } } } diff --git a/src/components/__tests__/DisplayName.test.js b/src/components/__tests__/DisplayName.test.js index 17921daf..dbd56c42 100644 --- a/src/components/__tests__/DisplayName.test.js +++ b/src/components/__tests__/DisplayName.test.js @@ -2,7 +2,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { mount } from '@vue/test-utils'; const mocks = vi.hoisted(() => ({ - fetch: vi.fn(() => Promise.resolve({ json: { displayName: 'Fetched User' } })), + fetch: vi.fn(() => + Promise.resolve({ json: { displayName: 'Fetched User' } }) + ), showUserDialog: vi.fn() })); @@ -52,7 +54,9 @@ describe('DisplayName.vue', () => { await flush(); - expect(mocks.fetch).toHaveBeenCalledWith('user.dialog', { userId: 'usr_2' }); + expect(mocks.fetch).toHaveBeenCalledWith('user.dialog', { + userId: 'usr_2' + }); expect(wrapper.text()).toBe('Fetched User'); }); diff --git a/src/components/__tests__/Emoji.test.js b/src/components/__tests__/Emoji.test.js index 4ac3f474..e84a358b 100644 --- a/src/components/__tests__/Emoji.test.js +++ b/src/components/__tests__/Emoji.test.js @@ -27,8 +27,13 @@ vi.mock('../../shared/utils', () => ({ vi.mock('../ui/avatar', () => ({ Avatar: { template: '
' }, - AvatarImage: { props: ['src'], template: '' }, - AvatarFallback: { template: '' } + AvatarImage: { + props: ['src'], + template: '' + }, + AvatarFallback: { + template: '' + } })); vi.mock('lucide-vue-next', () => ({ @@ -87,8 +92,12 @@ describe('Emoji.vue', () => { expect(mocks.getCachedEmoji).toHaveBeenCalledWith('file_1'); expect(wrapper.find('[data-testid="avatar"]').exists()).toBe(true); - expect(wrapper.find('[data-testid="avatar-image"]').attributes('src')).toBe('https://example.com/file_2.png'); - expect(wrapper.find('[data-testid="avatar-fallback"]').exists()).toBe(true); + expect( + wrapper.find('[data-testid="avatar-image"]').attributes('src') + ).toBe('https://example.com/file_2.png'); + expect(wrapper.find('[data-testid="avatar-fallback"]').exists()).toBe( + true + ); }); it('updates when imageUrl changes', async () => { diff --git a/src/components/__tests__/FullscreenImagePreview.test.js b/src/components/__tests__/FullscreenImagePreview.test.js index df09d1f0..6b52fcb3 100644 --- a/src/components/__tests__/FullscreenImagePreview.test.js +++ b/src/components/__tests__/FullscreenImagePreview.test.js @@ -2,20 +2,66 @@ import { describe, expect, it, vi } from 'vitest'; import { mount } from '@vue/test-utils'; const mocks = vi.hoisted(() => ({ - dialog: { value: { visible: true, imageUrl: 'https://example.com/a.png', fileName: 'a.png' } } + dialog: { + value: { + visible: true, + imageUrl: 'https://example.com/a.png', + fileName: 'a.png' + } + } })); vi.mock('pinia', async (i) => ({ ...(await i()), storeToRefs: (s) => s })); vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: (k) => k }) })); -vi.mock('@/stores/settings/general', () => ({ useGeneralSettingsStore: () => ({ disableGpuAcceleration: { value: false } }) })); -vi.mock('../../stores', () => ({ useGalleryStore: () => ({ fullscreenImageDialog: mocks.dialog, showFullscreenImageDialog: vi.fn() }) })); -vi.mock('@/lib/modalPortalLayers', () => ({ acquireModalPortalLayer: () => ({ element: 'body', bringToFront: vi.fn(), release: vi.fn() }) })); +vi.mock('@/stores/settings/general', () => ({ + useGeneralSettingsStore: () => ({ + disableGpuAcceleration: { value: false } + }) +})); +vi.mock('../../stores', () => ({ + useGalleryStore: () => ({ + fullscreenImageDialog: mocks.dialog, + showFullscreenImageDialog: vi.fn() + }) +})); +vi.mock('@/lib/modalPortalLayers', () => ({ + acquireModalPortalLayer: () => ({ + element: 'body', + bringToFront: vi.fn(), + release: vi.fn() + }) +})); vi.mock('@/lib/utils', () => ({ cn: (...a) => a.filter(Boolean).join(' ') })); -vi.mock('../../shared/utils', () => ({ escapeTag: (s) => s, extractFileId: () => 'f1' })); -vi.mock('vue-sonner', () => ({ toast: { info: vi.fn(() => 'id'), success: vi.fn(), error: vi.fn(), dismiss: vi.fn() } })); -vi.mock('@/components/ui/dialog', () => ({ Dialog: { template: '
' } })); -vi.mock('reka-ui', () => ({ DialogPortal: { template: '
' }, DialogOverlay: { template: '
' }, DialogContent: { emits: ['click'], template: '
' } })); -vi.mock('@/components/ui/button', () => ({ Button: { emits: ['click'], template: '' } })); +vi.mock('../../shared/utils', () => ({ + escapeTag: (s) => s, + extractFileId: () => 'f1' +})); +vi.mock('vue-sonner', () => ({ + toast: { + info: vi.fn(() => 'id'), + success: vi.fn(), + error: vi.fn(), + dismiss: vi.fn() + } +})); +vi.mock('@/components/ui/dialog', () => ({ + Dialog: { template: '
' } +})); +vi.mock('reka-ui', () => ({ + DialogPortal: { template: '
' }, + DialogOverlay: { template: '
' }, + DialogContent: { + emits: ['click'], + template: '
' + } +})); +vi.mock('@/components/ui/button', () => ({ + Button: { + emits: ['click'], + template: + '' + } +})); vi.mock('lucide-vue-next', () => ({ Copy: { template: '' }, Download: { template: '' }, diff --git a/src/components/__tests__/GlobalSearchDialog.test.js b/src/components/__tests__/GlobalSearchDialog.test.js index edb4b81f..3cdeaf4b 100644 --- a/src/components/__tests__/GlobalSearchDialog.test.js +++ b/src/components/__tests__/GlobalSearchDialog.test.js @@ -33,17 +33,35 @@ vi.mock('../../stores/globalSearch', () => ({ selectResult: (...args) => mocks.selectResult(...args) }) })); -vi.mock('../../composables/useUserDisplay', () => ({ useUserDisplay: () => ({ userImage: (...a) => mocks.userImage(...a) }) })); -vi.mock('../GlobalSearchSync.vue', () => ({ default: { template: '
' } })); -vi.mock('@/components/ui/dialog', () => ({ Dialog: { template: '
' }, DialogContent: { template: '
' }, DialogHeader: { template: '
' }, DialogTitle: { template: '
' }, DialogDescription: { template: '
' } })); +vi.mock('../../composables/useUserDisplay', () => ({ + useUserDisplay: () => ({ userImage: (...a) => mocks.userImage(...a) }) +})); +vi.mock('../GlobalSearchSync.vue', () => ({ + default: { template: '
' } +})); +vi.mock('@/components/ui/dialog', () => ({ + Dialog: { template: '
' }, + DialogContent: { template: '
' }, + DialogHeader: { template: '
' }, + DialogTitle: { template: '
' }, + DialogDescription: { template: '
' } +})); vi.mock('@/components/ui/command', () => ({ Command: { template: '
' }, CommandInput: { template: '' }, CommandList: { template: '
' }, CommandGroup: { template: '
' }, - CommandItem: { emits: ['select'], template: '' } + CommandItem: { + emits: ['select'], + template: + '' + } +})); +vi.mock('lucide-vue-next', () => ({ + Globe: { template: '' }, + Image: { template: '' }, + Users: { template: '' } })); -vi.mock('lucide-vue-next', () => ({ Globe: { template: '' }, Image: { template: '' }, Users: { template: '' } })); import GlobalSearchDialog from '../GlobalSearchDialog.vue'; diff --git a/src/components/__tests__/GlobalSearchSync.test.js b/src/components/__tests__/GlobalSearchSync.test.js index d8e483fe..8d2e8a79 100644 --- a/src/components/__tests__/GlobalSearchSync.test.js +++ b/src/components/__tests__/GlobalSearchSync.test.js @@ -12,7 +12,10 @@ const mocks = vi.hoisted(() => ({ } }, filterState: null, - allItemsEntries: [['a', {}], ['b', {}]], + allItemsEntries: [ + ['a', {}], + ['b', {}] + ], allGroupsEntries: [['g1', {}]] })); diff --git a/src/components/__tests__/InstanceActionBar.test.js b/src/components/__tests__/InstanceActionBar.test.js index 0736e9fa..7d983b8c 100644 --- a/src/components/__tests__/InstanceActionBar.test.js +++ b/src/components/__tests__/InstanceActionBar.test.js @@ -4,11 +4,18 @@ import { nextTick } from 'vue'; const mocks = vi.hoisted(() => ({ checkCanInviteSelf: vi.fn(() => true), - parseLocation: vi.fn(() => ({ isRealInstance: true, instanceId: 'inst_1', worldId: 'wrld_1', tag: 'wrld_1:inst_1' })), + parseLocation: vi.fn(() => ({ + isRealInstance: true, + instanceId: 'inst_1', + worldId: 'wrld_1', + tag: 'wrld_1:inst_1' + })), hasGroupPermission: vi.fn(() => false), formatDateFilter: vi.fn(() => 'formatted-date'), selfInvite: vi.fn(() => Promise.resolve({})), - closeInstance: vi.fn(() => Promise.resolve({ json: { id: 'inst_closed' } })), + closeInstance: vi.fn(() => + Promise.resolve({ json: { id: 'inst_closed' } }) + ), showUserDialog: vi.fn(), toastSuccess: vi.fn(), applyInstance: vi.fn(), @@ -18,7 +25,10 @@ const mocks = vi.hoisted(() => ({ instanceJoinHistory: { value: new Map() }, canOpenInstanceInGame: false, isOpeningInstance: false, - lastLocation: { location: 'wrld_here:111', playerList: new Set(['u1', 'u2']) }, + lastLocation: { + location: 'wrld_here:111', + playerList: new Set(['u1', 'u2']) + }, currentUser: { id: 'usr_me' }, cachedGroups: new Map() })); @@ -31,7 +41,9 @@ vi.mock('pinia', async (importOriginal) => { Object.fromEntries( Object.entries(store).map(([key, value]) => [ key, - key === 'instanceJoinHistory' ? value : value?.value ?? value + key === 'instanceJoinHistory' + ? value + : (value?.value ?? value) ]) ) }; @@ -104,7 +116,8 @@ vi.mock('../../coordinators/userCoordinator', () => ({ vi.mock('@/components/ui/button', () => ({ Button: { emits: ['click'], - template: '' + template: + '' } })); @@ -157,7 +170,8 @@ function mountBar(props = {}) { stubs: { TooltipWrapper: { props: ['content'], - template: '
{{ content }}
' + template: + '
{{ content }}
' }, Timer: { props: ['epoch'], @@ -185,8 +199,12 @@ describe('InstanceActionBar.vue', () => { mocks.applyInstance.mockClear(); mocks.showLaunchDialog.mockClear(); mocks.tryOpenInstanceInVrc.mockClear(); - mocks.modalConfirm.mockImplementation(() => Promise.resolve({ ok: true })); - mocks.instanceJoinHistory.value = new Map([['wrld_base:111', 1700000000]]); + mocks.modalConfirm.mockImplementation(() => + Promise.resolve({ ok: true }) + ); + mocks.instanceJoinHistory.value = new Map([ + ['wrld_base:111', 1700000000] + ]); mocks.canOpenInstanceInGame = false; mocks.isOpeningInstance = false; mocks.lastLocation.location = 'wrld_here:111'; @@ -203,8 +221,12 @@ describe('InstanceActionBar.vue', () => { }); expect(wrapper.findAll('[data-testid="btn"]')).toHaveLength(2); - expect(wrapper.text()).toContain('dialog.user.info.launch_invite_tooltip'); - expect(wrapper.text()).toContain('dialog.user.info.self_invite_tooltip'); + expect(wrapper.text()).toContain( + 'dialog.user.info.launch_invite_tooltip' + ); + expect(wrapper.text()).toContain( + 'dialog.user.info.self_invite_tooltip' + ); }); it('launch button opens launch dialog with resolved launchLocation', async () => { @@ -240,7 +262,9 @@ describe('InstanceActionBar.vue', () => { worldId: 'wrld_1', shortName: 'sn' }); - expect(mocks.toastSuccess).toHaveBeenCalledWith('message.invite.self_sent'); + expect(mocks.toastSuccess).toHaveBeenCalledWith( + 'message.invite.self_sent' + ); }); it('invite button opens in VRChat when canOpenInstanceInGame is true', async () => { @@ -256,7 +280,10 @@ describe('InstanceActionBar.vue', () => { await inviteBtn.trigger('click'); - expect(mocks.tryOpenInstanceInVrc).toHaveBeenCalledWith('wrld_1:inst_1', 'sn'); + expect(mocks.tryOpenInstanceInVrc).toHaveBeenCalledWith( + 'wrld_1:inst_1', + 'sn' + ); expect(mocks.selfInvite).not.toHaveBeenCalled(); }); @@ -302,7 +329,11 @@ describe('InstanceActionBar.vue', () => { } }); - const closeBtn = wrapper.findAll('button').find((btn) => btn.text().includes('dialog.user.info.close_instance')); + const closeBtn = wrapper + .findAll('button') + .find((btn) => + btn.text().includes('dialog.user.info.close_instance') + ); expect(closeBtn).toBeTruthy(); await closeBtn.trigger('click'); @@ -311,9 +342,14 @@ describe('InstanceActionBar.vue', () => { await nextTick(); expect(mocks.modalConfirm).toHaveBeenCalled(); - expect(mocks.closeInstance).toHaveBeenCalledWith({ location: 'wrld_close:444', hardClose: false }); + expect(mocks.closeInstance).toHaveBeenCalledWith({ + location: 'wrld_close:444', + hardClose: false + }); expect(mocks.applyInstance).toHaveBeenCalledWith({ id: 'inst_closed' }); - expect(mocks.toastSuccess).toHaveBeenCalledWith('message.instance.closed'); + expect(mocks.toastSuccess).toHaveBeenCalledWith( + 'message.instance.closed' + ); }); it('hides launch and invite buttons when invite-self is not allowed', () => { diff --git a/src/components/__tests__/LocationWorld.test.js b/src/components/__tests__/LocationWorld.test.js index 5b8f53ef..fbdce6b0 100644 --- a/src/components/__tests__/LocationWorld.test.js +++ b/src/components/__tests__/LocationWorld.test.js @@ -7,7 +7,11 @@ const mocks = vi.hoisted(() => ({ showLaunchDialog: vi.fn(), showGroupDialog: vi.fn(), getGroupName: vi.fn(() => Promise.resolve('Fetched Group')), - parseLocation: vi.fn(() => ({ isRealInstance: true, tag: 'wrld_1:inst_1', groupId: 'grp_1' })) + parseLocation: vi.fn(() => ({ + isRealInstance: true, + tag: 'wrld_1:inst_1', + groupId: 'grp_1' + })) })); vi.mock('pinia', async (importOriginal) => { @@ -102,13 +106,19 @@ describe('LocationWorld.vue', () => { mocks.showGroupDialog.mockClear(); mocks.getGroupName.mockClear(); mocks.parseLocation.mockClear(); - mocks.parseLocation.mockImplementation(() => ({ isRealInstance: true, tag: 'wrld_1:inst_1', groupId: 'grp_1' })); + mocks.parseLocation.mockImplementation(() => ({ + isRealInstance: true, + tag: 'wrld_1:inst_1', + groupId: 'grp_1' + })); }); it('renders translated access type and instance name', () => { const wrapper = mountComponent(); - expect(wrapper.text()).toContain('dialog.world.instance.friends #Instance Name'); + expect(wrapper.text()).toContain( + 'dialog.world.instance.friends #Instance Name' + ); expect(wrapper.find('.flags.eu').exists()).toBe(true); }); @@ -119,7 +129,10 @@ describe('LocationWorld.vue', () => { await wrapper.findAll('.cursor-pointer')[0].trigger('click'); - expect(mocks.showLaunchDialog).toHaveBeenCalledWith('wrld_1:inst_1', 'short-1'); + expect(mocks.showLaunchDialog).toHaveBeenCalledWith( + 'wrld_1:inst_1', + 'short-1' + ); }); it('shows group hint and opens group dialog', async () => { diff --git a/src/components/dialogs/AvatarDialog/AvatarDialog.vue b/src/components/dialogs/AvatarDialog/AvatarDialog.vue index 6924a85d..f92d0cbd 100644 --- a/src/components/dialogs/AvatarDialog/AvatarDialog.vue +++ b/src/components/dialogs/AvatarDialog/AvatarDialog.vue @@ -695,11 +695,7 @@ const platforms = []; if (ref.unityPackages) { for (const unityPackage of ref.unityPackages) { - if ( - unityPackage.variant && - unityPackage.variant !== 'standard' && - unityPackage.variant !== 'security' - ) { + if (unityPackage.variant && unityPackage.variant !== 'standard' && unityPackage.variant !== 'security') { // skip imposters continue; } diff --git a/src/components/dialogs/CustomNavDialog.vue b/src/components/dialogs/CustomNavDialog.vue index ea88232f..ae495a9b 100644 --- a/src/components/dialogs/CustomNavDialog.vue +++ b/src/components/dialogs/CustomNavDialog.vue @@ -426,12 +426,7 @@ if (byId) return byId; } - if ( - allowIndexFallback && - typeof entity.index === 'number' && - entity.index >= 0 && - entity.index < nodes.length - ) { + if (allowIndexFallback && typeof entity.index === 'number' && entity.index >= 0 && entity.index < nodes.length) { return nodes[entity.index] || null; } @@ -620,8 +615,7 @@ const sourceNode = (sourceIdSnapshot ? visibleNodes.find( - (node) => - node.id === sourceIdSnapshot && node.type === (sourceIsFolderSnapshot ? 'folder' : 'item') + (node) => node.id === sourceIdSnapshot && node.type === (sourceIsFolderSnapshot ? 'folder' : 'item') ) : null) || resolveNodeFromDnDEntity(source, visibleNodes); if (!sourceNode) return; diff --git a/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue b/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue index b29aa518..fed1a9a8 100644 --- a/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue +++ b/src/components/dialogs/GroupDialog/GroupDialogInfoTab.vue @@ -381,8 +381,7 @@ const { showFullscreenImageDialog } = useGalleryStore(); const instanceStore = useInstanceStore(); - const { pastCalenderEvents, upcomingCalenderEvents, updateFollowingCalendarData } = - useGroupCalendarEvents(groupDialog); + const { pastCalenderEvents, upcomingCalenderEvents, updateFollowingCalendarData } = useGroupCalendarEvents(groupDialog); /** * diff --git a/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue b/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue index 80363d47..a2a05131 100644 --- a/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue +++ b/src/components/dialogs/GroupDialog/GroupMemberModerationDialog.vue @@ -122,11 +122,7 @@ import { useI18n } from 'vue-i18n'; import { useAppearanceSettingsStore, useGalleryStore, useGroupStore, useUserStore } from '../../../stores'; - import { - applyGroupMember, - handleGroupMember, - handleGroupMemberProps - } from '../../../coordinators/groupCoordinator'; + import { applyGroupMember, handleGroupMember, handleGroupMemberProps } from '../../../coordinators/groupCoordinator'; import { hasGroupPermission } from '../../../shared/utils'; import { useUserDisplay } from '../../../composables/useUserDisplay'; import { groupDialogFilterOptions, groupDialogSortingOptions } from '../../../shared/constants'; diff --git a/src/components/dialogs/GroupDialog/GroupMemberModerationExportDialog.vue b/src/components/dialogs/GroupDialog/GroupMemberModerationExportDialog.vue index 4545ae6c..12785c26 100644 --- a/src/components/dialogs/GroupDialog/GroupMemberModerationExportDialog.vue +++ b/src/components/dialogs/GroupDialog/GroupMemberModerationExportDialog.vue @@ -74,13 +74,7 @@ { label: 'description', text: 'dialog.group_member_moderation.description' }, { label: 'data', text: 'dialog.group_member_moderation.data' } ]; - const checkedGroupLogsExportLogsOptions = ref([ - 'created_at', - 'eventType', - 'actorDisplayName', - 'description', - 'data' - ]); + const checkedGroupLogsExportLogsOptions = ref(['created_at', 'eventType', 'actorDisplayName', 'description', 'data']); /** * diff --git a/src/components/dialogs/GroupDialog/__tests__/GroupDialogPhotosTab.test.js b/src/components/dialogs/GroupDialog/__tests__/GroupDialogPhotosTab.test.js index 753cb796..5c9387c0 100644 --- a/src/components/dialogs/GroupDialog/__tests__/GroupDialogPhotosTab.test.js +++ b/src/components/dialogs/GroupDialog/__tests__/GroupDialogPhotosTab.test.js @@ -10,7 +10,7 @@ vi.mock('vue-i18n', () => ({ locale: require('vue').ref('en') }), createI18n: () => ({ - global: { t: (key) => key , locale: require('vue').ref('en') }, + global: { t: (key) => key, locale: require('vue').ref('en') }, install: vi.fn() }) })); diff --git a/src/components/dialogs/GroupDialog/__tests__/GroupModerationBulkActions.test.js b/src/components/dialogs/GroupDialog/__tests__/GroupModerationBulkActions.test.js index a4ab902f..9ec8e1d9 100644 --- a/src/components/dialogs/GroupDialog/__tests__/GroupModerationBulkActions.test.js +++ b/src/components/dialogs/GroupDialog/__tests__/GroupModerationBulkActions.test.js @@ -3,12 +3,11 @@ import { mount } from '@vue/test-utils'; vi.mock('vue-i18n', () => ({ useI18n: () => ({ - t: (key) => key - , - locale: require('vue').ref('en') - }), + t: (key) => key, + locale: require('vue').ref('en') + }), createI18n: () => ({ - global: { t: (key) => key , locale: require('vue').ref('en') }, + global: { t: (key) => key, locale: require('vue').ref('en') }, install: vi.fn() }) })); @@ -53,7 +52,8 @@ function mountComponent(props = {}) { Trash2: { template: '' }, X: { template: '' }, TooltipWrapper: { - template: '
' + template: + '
' } } } @@ -68,24 +68,32 @@ describe('GroupModerationBulkActions.vue', () => { describe('rendering', () => { test('renders user ID input field', () => { const wrapper = mountComponent(); - expect(wrapper.text()).toContain('dialog.group_member_moderation.user_id'); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.user_id' + ); }); test('renders selected users section', () => { const wrapper = mountComponent(); - expect(wrapper.text()).toContain('dialog.group_member_moderation.selected_users'); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.selected_users' + ); }); test('renders roles dropdown with available roles', () => { const wrapper = mountComponent(); - expect(wrapper.text()).toContain('dialog.group_member_moderation.selected_roles'); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.selected_roles' + ); }); test('renders action buttons', () => { const wrapper = mountComponent(); const text = wrapper.text(); expect(text).toContain('dialog.group_member_moderation.add_roles'); - expect(text).toContain('dialog.group_member_moderation.remove_roles'); + expect(text).toContain( + 'dialog.group_member_moderation.remove_roles' + ); expect(text).toContain('dialog.group_member_moderation.save_note'); expect(text).toContain('dialog.group_member_moderation.kick'); expect(text).toContain('dialog.group_member_moderation.ban'); @@ -95,8 +103,18 @@ describe('GroupModerationBulkActions.vue', () => { test('renders selected user badges', () => { const wrapper = mountComponent({ selectedUsersArray: [ - { id: 'usr_1', userId: 'usr_1', membershipStatus: 'member', user: { displayName: 'Alice' } }, - { id: 'usr_2', userId: 'usr_2', membershipStatus: 'member', user: { displayName: 'Bob' } } + { + id: 'usr_1', + userId: 'usr_1', + membershipStatus: 'member', + user: { displayName: 'Alice' } + }, + { + id: 'usr_2', + userId: 'usr_2', + membershipStatus: 'member', + user: { displayName: 'Bob' } + } ] }); expect(wrapper.text()).toContain('Alice'); @@ -106,54 +124,88 @@ describe('GroupModerationBulkActions.vue', () => { test('shows warning tooltip for non-member users', () => { const wrapper = mountComponent({ selectedUsersArray: [ - { id: 'usr_1', userId: 'usr_1', membershipStatus: 'banned', user: { displayName: 'Charlie' } } + { + id: 'usr_1', + userId: 'usr_1', + membershipStatus: 'banned', + user: { displayName: 'Charlie' } + } ] }); - expect(wrapper.text()).toContain('dialog.group_member_moderation.user_isnt_in_group'); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.user_isnt_in_group' + ); }); test('does not show warning for member users', () => { const wrapper = mountComponent({ selectedUsersArray: [ - { id: 'usr_1', userId: 'usr_1', membershipStatus: 'member', user: { displayName: 'Alice' } } + { + id: 'usr_1', + userId: 'usr_1', + membershipStatus: 'member', + user: { displayName: 'Alice' } + } ] }); - expect(wrapper.text()).not.toContain('dialog.group_member_moderation.user_isnt_in_group'); + expect(wrapper.text()).not.toContain( + 'dialog.group_member_moderation.user_isnt_in_group' + ); }); }); describe('progress indicator', () => { test('shows progress when progressCurrent > 0', () => { - const wrapper = mountComponent({ progressCurrent: 3, progressTotal: 10 }); - expect(wrapper.text()).toContain('dialog.group_member_moderation.progress'); + const wrapper = mountComponent({ + progressCurrent: 3, + progressTotal: 10 + }); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.progress' + ); expect(wrapper.text()).toContain('3/10'); }); test('shows cancel button during progress', () => { - const wrapper = mountComponent({ progressCurrent: 3, progressTotal: 10 }); - expect(wrapper.text()).toContain('dialog.group_member_moderation.cancel'); + const wrapper = mountComponent({ + progressCurrent: 3, + progressTotal: 10 + }); + expect(wrapper.text()).toContain( + 'dialog.group_member_moderation.cancel' + ); }); test('hides progress when not in progress', () => { const wrapper = mountComponent({ progressCurrent: 0 }); - expect(wrapper.text()).not.toContain('dialog.group_member_moderation.progress'); + expect(wrapper.text()).not.toContain( + 'dialog.group_member_moderation.progress' + ); }); }); describe('button disabled states', () => { test('add/remove roles disabled when no roles selected', () => { const wrapper = mountComponent({ selectedRoles: [] }); - const addBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.add_roles') - ); + const addBtn = wrapper + .findAll('button') + .find((b) => + b + .text() + .includes('dialog.group_member_moderation.add_roles') + ); expect(addBtn.attributes('disabled')).toBeDefined(); }); test('add/remove roles enabled when roles are selected', () => { const wrapper = mountComponent({ selectedRoles: ['role_1'] }); - const addBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.add_roles') - ); + const addBtn = wrapper + .findAll('button') + .find((b) => + b + .text() + .includes('dialog.group_member_moderation.add_roles') + ); expect(addBtn.attributes('disabled')).toBeUndefined(); }); @@ -163,25 +215,35 @@ describe('GroupModerationBulkActions.vue', () => { progressCurrent: 5, progressTotal: 10 }); - const kickBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.kick') - ); + const kickBtn = wrapper + .findAll('button') + .find((b) => + b.text().includes('dialog.group_member_moderation.kick') + ); expect(kickBtn.attributes('disabled')).toBeDefined(); }); test('select user button disabled when no user ID entered', () => { const wrapper = mountComponent({ selectUserId: '' }); - const selectBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.select_user') - ); + const selectBtn = wrapper + .findAll('button') + .find((b) => + b + .text() + .includes('dialog.group_member_moderation.select_user') + ); expect(selectBtn.attributes('disabled')).toBeDefined(); }); test('select user button enabled when user ID is entered', () => { const wrapper = mountComponent({ selectUserId: 'usr_test' }); - const selectBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.select_user') - ); + const selectBtn = wrapper + .findAll('button') + .find((b) => + b + .text() + .includes('dialog.group_member_moderation.select_user') + ); expect(selectBtn.attributes('disabled')).toBeUndefined(); }); }); @@ -194,9 +256,11 @@ describe('GroupModerationBulkActions.vue', () => { _mockPermissions: ['group-bans-manage'] } }); - const kickBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.kick') - ); + const kickBtn = wrapper + .findAll('button') + .find((b) => + b.text().includes('dialog.group_member_moderation.kick') + ); expect(kickBtn.attributes('disabled')).toBeDefined(); }); @@ -207,12 +271,16 @@ describe('GroupModerationBulkActions.vue', () => { _mockPermissions: ['group-members-remove'] } }); - const banBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.ban') - ); - const unbanBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.unban') - ); + const banBtn = wrapper + .findAll('button') + .find((b) => + b.text().includes('dialog.group_member_moderation.ban') + ); + const unbanBtn = wrapper + .findAll('button') + .find((b) => + b.text().includes('dialog.group_member_moderation.unban') + ); expect(banBtn.attributes('disabled')).toBeDefined(); expect(unbanBtn.attributes('disabled')).toBeDefined(); }); @@ -221,9 +289,13 @@ describe('GroupModerationBulkActions.vue', () => { describe('events', () => { test('emits select-user on select button click', async () => { const wrapper = mountComponent({ selectUserId: 'usr_test' }); - const selectBtn = wrapper.findAll('button').find((b) => - b.text().includes('dialog.group_member_moderation.select_user') - ); + const selectBtn = wrapper + .findAll('button') + .find((b) => + b + .text() + .includes('dialog.group_member_moderation.select_user') + ); await selectBtn.trigger('click'); expect(wrapper.emitted('select-user')).toBeTruthy(); }); @@ -241,7 +313,12 @@ describe('GroupModerationBulkActions.vue', () => { }); test('emits delete-user when removing a selected user', async () => { - const user = { id: 'usr_1', userId: 'usr_1', membershipStatus: 'member', user: { displayName: 'Alice' } }; + const user = { + id: 'usr_1', + userId: 'usr_1', + membershipStatus: 'member', + user: { displayName: 'Alice' } + }; const wrapper = mountComponent({ selectedUsersArray: [user] }); // The X button is a native ' } })); -vi.mock('@/components/ui/input-group', () => ({ InputGroupTextareaField: { props: ['modelValue'], emits: ['update:modelValue'], template: '