Add pre-commit formatting hooks for test files and configure Husky integration:

- Added Husky pre-commit hook to enforce formatting on JavaScript and TypeScript test files.
- Implemented `pre-commit-format-tests.mjs` to check for unstaged changes, format test files using Prettier, and re-stage them.
- Included `prepare-husky.mjs` to verify Husky installation in the git worktree.
- Updated `package-lock.json` to include `husky` as a dependency.
This commit is contained in:
Jeppe Bundgaard
2026-04-15 12:18:49 +02:00
parent a0c8574399
commit a9a5137515
7 changed files with 262 additions and 66 deletions
+2
View File
@@ -0,0 +1,2 @@
cd -- "$(dirname -- "$0")/.."
npm run format:tests:commit
+100 -63
View File
@@ -69,6 +69,7 @@
"@vitejs/plugin-vue": "^6.0.5",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/test-utils": "^2.4.6",
"husky": "^9.1.7",
"jsdom": "^29.0.0",
"otpauth": "^9.5.0",
"prettier": "2.8.8",
@@ -230,8 +231,8 @@
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -261,6 +262,7 @@
"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"
@@ -556,6 +558,7 @@
"version": "7.29.2",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz",
"integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.28.6",
@@ -1947,13 +1950,6 @@
"node": ">=0.12.0"
}
},
"node_modules/@bubblewrap/core/node_modules/undici-types": {
"version": "7.19.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
"license": "MIT",
"optional": true
},
"node_modules/@bubblewrap/validator": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@bubblewrap/validator/-/validator-1.22.0.tgz",
@@ -1971,7 +1967,7 @@
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
"integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
"devOptional": true,
"dev": true,
"license": "(Apache-2.0 AND BSD-3-Clause)"
},
"node_modules/@creativebulma/bulma-badge": {
@@ -2075,7 +2071,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20.19.0"
},
@@ -2124,7 +2119,6 @@
}
],
"license": "MIT",
"peer": true,
"engines": {
"node": ">=20.19.0"
}
@@ -2136,6 +2130,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2152,6 +2147,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2168,6 +2164,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2184,6 +2181,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2200,6 +2198,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2216,6 +2215,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2232,6 +2232,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2248,6 +2249,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2264,6 +2266,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2280,6 +2283,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2296,6 +2300,7 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2312,6 +2317,7 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2328,6 +2334,7 @@
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2344,6 +2351,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2360,6 +2368,7 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2376,6 +2385,7 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2392,6 +2402,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2408,6 +2419,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2424,6 +2436,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2440,6 +2453,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2456,6 +2470,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2472,6 +2487,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2488,6 +2504,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2504,6 +2521,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2520,6 +2538,7 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -2562,7 +2581,6 @@
"resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.20.tgz",
"integrity": "sha512-1cukXLlePFiJ8YKXn/4tMKsy0etxYLCkXk8nUCFi11nRONF2Ba2CD5b21/ovtOO2tL6afTJfwmc1ed3HG7eB1g==",
"license": "MIT",
"peer": true,
"dependencies": {
"preact": "~10.12.1"
}
@@ -2806,7 +2824,6 @@
"resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz",
"integrity": "sha512-xcmww1O/JFP2MrlGUMd3Q78S3Qu6W3mYTXYuIqFq33EorgYHV/HqymHfXy9GjiCJ7OI+7lWx6nYFOzU7M4rd1Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/core": "^0.22.12"
}
@@ -2843,7 +2860,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.22.12.tgz",
"integrity": "sha512-xslz2ZoFZOPLY8EZ4dC29m168BtDx95D6K80TzgUi8gqT7LY6CsajWO0FAxDwHz6h0eomHMfyGX0stspBrTKnQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -2856,7 +2872,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.22.12.tgz",
"integrity": "sha512-S0vJADTuh1Q9F+cXAwFPlrKWzDj2F9t/9JAbUvaaDuivpyWuImEKXVz5PUZw2NbpuSHjwssbTpOZ8F13iJX4uw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -2881,7 +2896,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.22.12.tgz",
"integrity": "sha512-xImhTE5BpS8xa+mAN6j4sMRWaUgUDLoaGHhJhpC+r7SKKErYDR0WQV4yCE4gP+N0gozD0F3Ka1LUSaMXrn7ZIA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12",
"tinycolor2": "^1.6.0"
@@ -2925,7 +2939,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.22.12.tgz",
"integrity": "sha512-FNuUN0OVzRCozx8XSgP9MyLGMxNHHJMFt+LJuFjn1mu3k0VQxrzqbN06yIl46TVejhyAhcq5gLzqmSCHvlcBVw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3049,7 +3062,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.22.12.tgz",
"integrity": "sha512-3NyTPlPbTnGKDIbaBgQ3HbE6wXbAlFfxHVERmrbqAi8R3r6fQPxpCauA8UVDnieg5eo04D0T8nnnNIX//i/sXg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3062,7 +3074,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.22.12.tgz",
"integrity": "sha512-9YNEt7BPAFfTls2FGfKBVgwwLUuKqy+E8bDGGEsOqHtbuhbshVGxN2WMZaD4gh5IDWvR+emmmPPWGgaYNYt1gA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3078,7 +3089,6 @@
"resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.22.12.tgz",
"integrity": "sha512-dghs92qM6MhHj0HrV2qAwKPMklQtjNpoYgAB94ysYpsXslhRTiPisueSIELRwZGEr0J0VUxpUY7HgJwlSIgGZw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@jimp/utils": "^0.22.12"
},
@@ -3213,6 +3223,7 @@
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -3232,7 +3243,7 @@
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
@@ -3941,6 +3952,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3954,6 +3966,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3967,6 +3980,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3980,6 +3994,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -3993,6 +4008,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4006,6 +4022,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4019,6 +4036,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4032,6 +4050,7 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4045,6 +4064,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4058,6 +4078,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4071,6 +4092,7 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4084,6 +4106,7 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4097,6 +4120,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4110,6 +4134,7 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4123,6 +4148,7 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4136,6 +4162,7 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4149,6 +4176,7 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4162,6 +4190,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4175,6 +4204,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4188,6 +4218,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4201,6 +4232,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4214,6 +4246,7 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4227,6 +4260,7 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4240,6 +4274,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -4368,6 +4403,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/event-calendar__core": {
@@ -5339,9 +5375,8 @@
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
"integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
"devOptional": true,
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -5376,7 +5411,6 @@
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@@ -5470,8 +5504,7 @@
"version": "5.10.4",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-5.10.4.tgz",
"integrity": "sha512-gt0VUqZ2+mr25ScbUcKZgJr96jKYm4vjOcxEWCEh/E5F4dWqhyo3dBhPRvNNnkKiWxkMd2cBwj3ZYH3rK39fkA==",
"license": "SEE LICENSE IN LICENSE",
"peer": true
"license": "SEE LICENSE IN LICENSE"
},
"node_modules/aria-query": {
"version": "5.3.1",
@@ -5595,7 +5628,6 @@
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
@@ -5810,7 +5842,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5895,8 +5926,7 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.4.tgz",
"integrity": "sha512-Ffb6YGXDiZYX3cqvSbHWqQ8+LkX6tVoTcZuVB3lm93sbAVXlO0D6QlOTMnV6g18gILpAXqkG2z9hf9z4hCjz2g==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/bulma-block-list": {
"version": "1.1.0",
@@ -6154,7 +6184,6 @@
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@kurkle/color": "^0.3.0"
},
@@ -6343,7 +6372,7 @@
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
"integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/colors": {
@@ -6417,6 +6446,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/copy-anything": {
@@ -6568,7 +6598,6 @@
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC",
"peer": true,
"engines": {
"node": ">=12"
}
@@ -6757,7 +6786,6 @@
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
@@ -7195,6 +7223,7 @@
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -7239,6 +7268,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -7516,6 +7546,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -7770,6 +7801,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -7863,6 +7895,7 @@
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -8259,6 +8292,22 @@
"node": ">=18.18.0"
}
},
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
"license": "MIT",
"bin": {
"husky": "bin.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/iconv-lite": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
@@ -8286,8 +8335,7 @@
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
"integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
"license": "Apache-2.0",
"peer": true
"license": "Apache-2.0"
},
"node_modules/ieee754": {
"version": "1.2.1",
@@ -9192,6 +9240,7 @@
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
"bin": {
"json5": "lib/cli.js"
@@ -9552,16 +9601,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"peer": true,
"engines": {
"node": "*"
}
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
@@ -10126,6 +10165,7 @@
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -10354,7 +10394,6 @@
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
"license": "MIT",
"peer": true,
"dependencies": {
"dijkstrajs": "^1.0.1",
"pngjs": "^5.0.0",
@@ -10708,8 +10747,8 @@
"version": "4.60.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz",
"integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
@@ -10756,6 +10795,7 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -10893,7 +10933,6 @@
"resolved": "https://registry.npmjs.org/sass/-/sass-1.98.0.tgz",
"integrity": "sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A==",
"license": "MIT",
"peer": true,
"dependencies": {
"chokidar": "^4.0.0",
"immutable": "^5.1.5",
@@ -10913,9 +10952,8 @@
"version": "1.98.0",
"resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.98.0.tgz",
"integrity": "sha512-Do7u6iRb6K+lrllcTkB1BXcHwOxcKe3rEfOF/GcCLE2w3WpddakRAosJOHFUR37DpsvimQXEt5abs3NzUjEIqg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@bufbuild/protobuf": "^2.5.0",
"colorjs.io": "^0.5.0",
@@ -11262,7 +11300,7 @@
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
@@ -11555,7 +11593,7 @@
"version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
"integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"buffer-from": "^1.0.0",
@@ -11566,7 +11604,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"devOptional": true,
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
@@ -11976,7 +12014,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
"integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
"devOptional": true,
"dev": true,
"license": "MIT",
"dependencies": {
"sync-message-port": "^1.0.0"
@@ -11989,7 +12027,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz",
"integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==",
"devOptional": true,
"dev": true,
"license": "MIT",
"engines": {
"node": ">=16.0.0"
@@ -12086,7 +12124,7 @@
"version": "5.46.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz",
"integrity": "sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ==",
"devOptional": true,
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
@@ -12105,7 +12143,7 @@
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/tesseract.js": {
@@ -12197,6 +12235,7 @@
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -12655,15 +12694,15 @@
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
"devOptional": true,
"dev": true,
"license": "MIT"
},
"node_modules/vite": {
"version": "7.1.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz",
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -13485,6 +13524,7 @@
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
@@ -14056,7 +14096,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.21.3",
"postcss": "^8.4.43",
@@ -14116,7 +14155,6 @@
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.31.tgz",
"integrity": "sha512-iV/sU9SzOlmA/0tygSmjkEN6Jbs3nPoIPFhCMLD2STrjgOU8DX7ZtzMhg4ahVwf5Rp9KoFzcXeB1ZrVbLBp5/Q==",
"license": "MIT",
"peer": true,
"dependencies": {
"@vue/compiler-dom": "3.5.31",
"@vue/compiler-sfc": "3.5.31",
@@ -14804,7 +14842,6 @@
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
+3
View File
@@ -8,7 +8,9 @@
"build": "vite build",
"build:dev": "vite build --mode development",
"format:tests": "prettier --write \"tests/**/*.{js,ts}\"",
"format:tests:commit": "node scripts/pre-commit-format-tests.mjs",
"format:tests:check": "prettier --check \"tests/**/*.{js,ts}\"",
"prepare": "node scripts/prepare-husky.mjs",
"postinstall": "node scripts/postinstall-sync-playwright-root-links.mjs",
"preview": "vite preview",
"preview:prod": "npm run build && npm run preview -- --host 127.0.0.1 --port 4173",
@@ -90,6 +92,7 @@
"@vitejs/plugin-vue": "^6.0.5",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/test-utils": "^2.4.6",
"husky": "^9.1.7",
"jsdom": "^29.0.0",
"otpauth": "^9.5.0",
"prettier": "2.8.8",
+111
View File
@@ -0,0 +1,111 @@
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, "..");
const gitPathspecs = [":(glob)tests/**/*.js", ":(glob)tests/**/*.ts"];
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: repoRoot,
encoding: "utf8",
...options,
});
if (result.error) {
throw result.error;
}
return result;
}
function fail(message) {
console.error(message);
process.exit(1);
}
function runNpmScript(scriptName) {
if (process.env.npm_execpath) {
return run(process.execPath, [process.env.npm_execpath, "run", scriptName], { stdio: "inherit" });
}
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
return run(npmCommand, ["run", scriptName], {
shell: process.platform === "win32",
stdio: "inherit",
});
}
function parseLines(output) {
return output
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean);
}
const worktreeResult = run("git", ["rev-parse", "--show-toplevel"]);
if (worktreeResult.status !== 0) {
fail("Pre-commit formatting must run inside the front-end-vue Git worktree.");
}
const gitRoot = path.resolve(worktreeResult.stdout.trim());
if (gitRoot !== repoRoot) {
fail(`Expected front-end-vue to be the Git root, but git reported: ${gitRoot}`);
}
const unstagedTrackedResult = run("git", ["diff", "--name-only", "--", ...gitPathspecs]);
if (unstagedTrackedResult.status !== 0) {
fail("Unable to inspect unstaged test-file changes before running Prettier.");
}
const untrackedResult = run("git", ["ls-files", "--others", "--exclude-standard", "--", ...gitPathspecs]);
if (untrackedResult.status !== 0) {
fail("Unable to inspect untracked test files before running Prettier.");
}
const unsafePaths = [
...parseLines(unstagedTrackedResult.stdout),
...parseLines(untrackedResult.stdout),
];
if (unsafePaths.length > 0) {
const formattedPaths = unsafePaths.map((filePath) => ` - ${filePath}`).join("\n");
fail(
[
"Pre-commit formatting aborted because tests contain unstaged changes.",
"Stage or discard these files before committing so the hook does not add unrelated work:",
formattedPaths,
].join("\n")
);
}
console.log("Running Prettier for frontend test files...");
const formatResult = runNpmScript("format:tests");
if (formatResult.status !== 0) {
process.exit(formatResult.status ?? 1);
}
console.log("Restaging formatted frontend test files...");
const knownFilesResult = run("git", ["ls-files", "--cached", "--others", "--exclude-standard", "--", ...gitPathspecs]);
if (knownFilesResult.status !== 0) {
fail("Prettier completed, but the hook could not resolve test files for restaging.");
}
const filesToStage = parseLines(knownFilesResult.stdout);
if (filesToStage.length === 0) {
process.exit(0);
}
const addResult = run("git", ["add", "--", ...filesToStage], { stdio: "inherit" });
if (addResult.status !== 0) {
fail("Prettier completed, but git add failed while restaging formatted test files.");
}
+38
View File
@@ -0,0 +1,38 @@
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(scriptDir, "..");
const huskyBin = path.join(repoRoot, "node_modules", "husky", "bin.js");
function run(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: repoRoot,
encoding: "utf8",
...options,
});
if (result.error) {
throw result.error;
}
return result;
}
const worktreeResult = run("git", ["rev-parse", "--show-toplevel"]);
if (worktreeResult.status !== 0) {
console.log("Skipping Husky install because front-end-vue is not attached to a Git worktree.");
process.exit(0);
}
const gitRoot = path.resolve(worktreeResult.stdout.trim());
if (gitRoot !== repoRoot) {
console.log(`Skipping Husky install because Git root is ${gitRoot}, not ${repoRoot}.`);
process.exit(0);
}
const huskyResult = run(process.execPath, [huskyBin], { stdio: "inherit" });
process.exit(huskyResult.status ?? 1);
+4 -1
View File
@@ -645,7 +645,10 @@ function mergeEdgeGatewayFixtureRecord(baseGateway, overrides = {}) {
function createEdgeGatewayFixture(options = {}) {
const primaryBrokerConnected = options.brokerConnected ?? true;
const gatewayOverridesById = new Map(
(Array.isArray(options.gatewayOverrides) ? options.gatewayOverrides : []).map((gateway) => [Number(gateway.id), gateway])
(Array.isArray(options.gatewayOverrides) ? options.gatewayOverrides : []).map((gateway) => [
Number(gateway.id),
gateway,
])
);
const baseGateways = [
{
+4 -2
View File
@@ -101,9 +101,11 @@ describe("edge gateway workspace contract", () => {
});
it("keeps route selection in the page layer and lets the department page deep-link into the canonical detail page", () => {
expect(workspaceSource).toContain('selectedGatewayId: {');
expect(workspaceSource).toContain("selectedGatewayId: {");
expect(workspaceSource).toContain('defineEmits(["open-gateway-page", "select-gateway"])');
expect(workspaceSource).toContain("const routeSelectedGatewayId = computed(() => normalizeGatewayId(props.selectedGatewayId));");
expect(workspaceSource).toContain(
"const routeSelectedGatewayId = computed(() => normalizeGatewayId(props.selectedGatewayId));"
);
expect(workspaceSource).toContain('data-testid="gateway-unavailable-state"');
expect(edgeGatewaysViewSource).toContain('route.name === "edgegatewaydetail"');
expect(edgeGatewaysViewSource).toContain(':selected-gateway-id="selectedGatewayId"');