test(vitest): amplia la suite con test unitari, integrazione, componenti e stress
- aggiunge test per gameState e utilita server - aggiunge test di integrazione WebSocket - aggiunge test componenti Vue (ControllerPage/DisplayPage) - aggiunge test stress su carico WebSocket - aggiorna configurazione Vitest per includere nuove cartelle e ambiente componenti - aggiorna script npm e dipendenze di test
This commit is contained in:
601
package-lock.json
generated
601
package-lock.json
generated
@@ -15,11 +15,14 @@
|
|||||||
"ws": "^8.19.0"
|
"ws": "^8.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "^4.11.1",
|
||||||
"@playwright/test": "^1.58.2",
|
"@playwright/test": "^1.58.2",
|
||||||
"@types/node": "^25.2.3",
|
"@types/node": "^25.2.3",
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
"@vitest/ui": "^4.0.18",
|
"@vitest/ui": "^4.0.18",
|
||||||
|
"@vue/test-utils": "^2.4.6",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
|
"happy-dom": "^20.6.1",
|
||||||
"jsdom": "^28.0.0",
|
"jsdom": "^28.0.0",
|
||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-pwa": "^0.16.0",
|
"vite-plugin-pwa": "^0.16.0",
|
||||||
@@ -112,6 +115,18 @@
|
|||||||
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
"integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@axe-core/playwright": {
|
||||||
|
"version": "4.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.11.1.tgz",
|
||||||
|
"integrity": "sha512-mKEfoUIB1MkVTht0BGZFXtSAEKXMJoDkyV5YZ9jbBmZCcWDz71tegNsdTkIN8zc/yMi5Gm2kx7Z5YQ9PfWNAWw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"axe-core": "~4.11.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"playwright-core": ">= 1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/code-frame": {
|
"node_modules/@babel/code-frame": {
|
||||||
"version": "7.21.4",
|
"version": "7.21.4",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
|
||||||
@@ -2336,6 +2351,102 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@isaacs/cliui": {
|
||||||
|
"version": "8.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||||
|
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^5.1.2",
|
||||||
|
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||||
|
"strip-ansi": "^7.0.1",
|
||||||
|
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||||
|
"wrap-ansi": "^8.1.0",
|
||||||
|
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
|
||||||
|
"version": "6.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||||
|
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/ansi-styles": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/emoji-regex": {
|
||||||
|
"version": "9.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||||
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/string-width": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"eastasianwidth": "^0.2.0",
|
||||||
|
"emoji-regex": "^9.2.2",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
|
||||||
|
"version": "8.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||||
|
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^6.1.0",
|
||||||
|
"string-width": "^5.0.1",
|
||||||
|
"strip-ansi": "^7.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
@@ -2434,6 +2545,22 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@one-ini/wasm": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@pkgjs/parseargs": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||||
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.58.2",
|
"version": "1.58.2",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
|
||||||
@@ -2941,6 +3068,21 @@
|
|||||||
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==",
|
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/whatwg-mimetype": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/ws": {
|
||||||
|
"version": "8.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||||
|
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@vitejs/plugin-vue": {
|
"node_modules/@vitejs/plugin-vue": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.2.3.tgz",
|
||||||
@@ -3149,6 +3291,25 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.28.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.28.tgz",
|
||||||
"integrity": "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="
|
"integrity": "sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/test-utils": {
|
||||||
|
"version": "2.4.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz",
|
||||||
|
"integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"js-beautify": "^1.14.9",
|
||||||
|
"vue-component-type-helpers": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/abbrev": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
||||||
@@ -3268,6 +3429,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axe-core": {
|
||||||
|
"version": "4.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.1.tgz",
|
||||||
|
"integrity": "sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-plugin-polyfill-corejs2": {
|
"node_modules/babel-plugin-polyfill-corejs2": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.3.tgz",
|
||||||
@@ -3666,6 +3836,16 @@
|
|||||||
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
"url": "https://github.com/chalk/supports-color?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/config-chain": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ini": "^1.3.4",
|
||||||
|
"proto-list": "~1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
||||||
@@ -3721,6 +3901,20 @@
|
|||||||
"url": "https://opencollective.com/core-js"
|
"url": "https://opencollective.com/core-js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cross-spawn": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"path-key": "^3.1.0",
|
||||||
|
"shebang-command": "^2.0.0",
|
||||||
|
"which": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/crypto-random-string": {
|
"node_modules/crypto-random-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||||
@@ -3888,6 +4082,66 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eastasianwidth": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||||
|
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/editorconfig": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@one-ini/wasm": "0.1.1",
|
||||||
|
"commander": "^10.0.0",
|
||||||
|
"minimatch": "9.0.1",
|
||||||
|
"semver": "^7.5.3"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"editorconfig": "bin/editorconfig"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/editorconfig/node_modules/commander": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/editorconfig/node_modules/minimatch": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/editorconfig/node_modules/semver": {
|
||||||
|
"version": "7.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||||
|
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||||
|
"dev": true,
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@@ -4295,6 +4549,22 @@
|
|||||||
"is-callable": "^1.1.3"
|
"is-callable": "^1.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/foreground-child": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"cross-spawn": "^7.0.6",
|
||||||
|
"signal-exit": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -4529,6 +4799,44 @@
|
|||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/happy-dom": {
|
||||||
|
"version": "20.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.6.1.tgz",
|
||||||
|
"integrity": "sha512-+0vhESXXhFwkdjZnJ5DlmJIfUYGgIEEjzIjB+aKJbFuqlvvKyOi+XkI1fYbgYR9QCxG5T08koxsQ6HrQfa5gCQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": ">=20.0.0",
|
||||||
|
"@types/whatwg-mimetype": "^3.0.2",
|
||||||
|
"@types/ws": "^8.18.1",
|
||||||
|
"entities": "^6.0.1",
|
||||||
|
"whatwg-mimetype": "^3.0.0",
|
||||||
|
"ws": "^8.18.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/happy-dom/node_modules/entities": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/happy-dom/node_modules/whatwg-mimetype": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has": {
|
"node_modules/has": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||||
@@ -4713,6 +5021,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/ini": {
|
||||||
|
"version": "1.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
|
||||||
|
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/internal-slot": {
|
"node_modules/internal-slot": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz",
|
||||||
@@ -5018,6 +5332,27 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/isexe": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/jackspeak": {
|
||||||
|
"version": "3.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||||
|
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@isaacs/cliui": "^8.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@pkgjs/parseargs": "^0.11.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jake": {
|
"node_modules/jake": {
|
||||||
"version": "10.8.7",
|
"version": "10.8.7",
|
||||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
|
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
|
||||||
@@ -5141,6 +5476,72 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-beautify": {
|
||||||
|
"version": "1.15.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
|
||||||
|
"integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"config-chain": "^1.1.13",
|
||||||
|
"editorconfig": "^1.0.4",
|
||||||
|
"glob": "^10.4.2",
|
||||||
|
"js-cookie": "^3.0.5",
|
||||||
|
"nopt": "^7.2.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"css-beautify": "js/bin/css-beautify.js",
|
||||||
|
"html-beautify": "js/bin/html-beautify.js",
|
||||||
|
"js-beautify": "js/bin/js-beautify.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-beautify/node_modules/glob": {
|
||||||
|
"version": "10.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||||
|
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||||
|
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"foreground-child": "^3.1.0",
|
||||||
|
"jackspeak": "^3.1.2",
|
||||||
|
"minimatch": "^9.0.4",
|
||||||
|
"minipass": "^7.1.2",
|
||||||
|
"package-json-from-dist": "^1.0.0",
|
||||||
|
"path-scurry": "^1.11.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"glob": "dist/esm/bin.mjs"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-beautify/node_modules/minimatch": {
|
||||||
|
"version": "9.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||||
|
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/js-cookie": {
|
||||||
|
"version": "3.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
|
||||||
|
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
@@ -5428,6 +5829,15 @@
|
|||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minipass": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mrmime": {
|
"node_modules/mrmime": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||||
@@ -5473,6 +5883,21 @@
|
|||||||
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
|
"integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/nopt": {
|
||||||
|
"version": "7.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz",
|
||||||
|
"integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"abbrev": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"nopt": "bin/nopt.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
@@ -5540,6 +5965,12 @@
|
|||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/package-json-from-dist": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/parse5": {
|
"node_modules/parse5": {
|
||||||
"version": "8.0.0",
|
"version": "8.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
|
||||||
@@ -5581,12 +6012,43 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path-key": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-parse": {
|
"node_modules/path-parse": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/path-scurry": {
|
||||||
|
"version": "1.11.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||||
|
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"lru-cache": "^10.2.0",
|
||||||
|
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16 || 14 >=14.18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/path-scurry/node_modules/lru-cache": {
|
||||||
|
"version": "10.4.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||||
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/path-to-regexp": {
|
"node_modules/path-to-regexp": {
|
||||||
"version": "8.3.0",
|
"version": "8.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
|
||||||
@@ -5702,6 +6164,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proto-list": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||||
|
"integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -6116,6 +6584,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/shebang-command": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"shebang-regex": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/shebang-regex": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shell-quote": {
|
"node_modules/shell-quote": {
|
||||||
"version": "1.8.3",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
|
||||||
@@ -6202,6 +6691,18 @@
|
|||||||
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
|
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/signal-exit": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/sirv": {
|
"node_modules/sirv": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||||
@@ -6296,6 +6797,21 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-width-cjs": {
|
||||||
|
"name": "string-width",
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string.prototype.matchall": {
|
"node_modules/string.prototype.matchall": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
|
||||||
@@ -6386,6 +6902,19 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strip-ansi-cjs": {
|
||||||
|
"name": "strip-ansi",
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-comments": {
|
"node_modules/strip-comments": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz",
|
||||||
@@ -7582,6 +8111,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-component-type-helpers": {
|
||||||
|
"version": "2.2.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
|
||||||
|
"integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/vue-router": {
|
"node_modules/vue-router": {
|
||||||
"version": "4.6.4",
|
"version": "4.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz",
|
||||||
@@ -7645,6 +8180,21 @@
|
|||||||
"webidl-conversions": "^4.0.2"
|
"webidl-conversions": "^4.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"isexe": "^2.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"node-which": "bin/node-which"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which-boxed-primitive": {
|
"node_modules/which-boxed-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
|
||||||
@@ -7917,6 +8467,57 @@
|
|||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wrap-ansi-cjs": {
|
||||||
|
"name": "wrap-ansi",
|
||||||
|
"version": "7.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi-cjs/node_modules/color-convert": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "~1.1.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/wrap-ansi-cjs/node_modules/color-name": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
"node_modules/wrap-ansi/node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -11,10 +11,13 @@
|
|||||||
"serve": "vite build && node server.js",
|
"serve": "vite build && node server.js",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:unit": "vitest run tests/unit tests/integration",
|
"test:unit": "vitest run tests/unit tests/integration",
|
||||||
|
"test:component": "vitest run tests/component",
|
||||||
|
"test:stress": "vitest run tests/stress",
|
||||||
|
"test:all": "vitest run",
|
||||||
"test:ui": "vitest --ui",
|
"test:ui": "vitest --ui",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test --config=playwright.config.cjs",
|
||||||
"test:e2e:ui": "playwright test --ui",
|
"test:e2e:ui": "playwright test --config=playwright.config.cjs --ui",
|
||||||
"test:e2e:codegen": "playwright codegen"
|
"test:e2e:codegen": "playwright codegen --config=playwright.config.cjs"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
@@ -24,14 +27,17 @@
|
|||||||
"ws": "^8.19.0"
|
"ws": "^8.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@axe-core/playwright": "^4.11.1",
|
||||||
"@playwright/test": "^1.58.2",
|
"@playwright/test": "^1.58.2",
|
||||||
"@types/node": "^25.2.3",
|
"@types/node": "^25.2.3",
|
||||||
"@vitejs/plugin-vue": "^4.1.0",
|
"@vitejs/plugin-vue": "^4.1.0",
|
||||||
"@vitest/ui": "^4.0.18",
|
"@vitest/ui": "^4.0.18",
|
||||||
|
"@vue/test-utils": "^2.4.6",
|
||||||
"concurrently": "^9.2.1",
|
"concurrently": "^9.2.1",
|
||||||
|
"happy-dom": "^20.6.1",
|
||||||
"jsdom": "^28.0.0",
|
"jsdom": "^28.0.0",
|
||||||
"vite": "^4.3.9",
|
"vite": "^4.3.9",
|
||||||
"vite-plugin-pwa": "^0.16.0",
|
"vite-plugin-pwa": "^0.16.0",
|
||||||
"vitest": "^4.0.18"
|
"vitest": "^4.0.18"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
255
tests/component/ControllerPage.test.js
Normal file
255
tests/component/ControllerPage.test.js
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// @vitest-environment happy-dom
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import ControllerPage from '../../src/components/ControllerPage.vue'
|
||||||
|
|
||||||
|
// Mock globale WebSocket per jsdom
|
||||||
|
class MockWebSocket {
|
||||||
|
static OPEN = 1
|
||||||
|
static CONNECTING = 0
|
||||||
|
readyState = 0
|
||||||
|
onopen = null
|
||||||
|
onclose = null
|
||||||
|
onmessage = null
|
||||||
|
onerror = null
|
||||||
|
send = vi.fn()
|
||||||
|
close = vi.fn()
|
||||||
|
constructor() {
|
||||||
|
// Simula connessione immediata
|
||||||
|
setTimeout(() => {
|
||||||
|
this.readyState = 1
|
||||||
|
if (this.onopen) this.onopen()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.stubGlobal('WebSocket', MockWebSocket)
|
||||||
|
|
||||||
|
// Helper per creare il componente con stato personalizzato
|
||||||
|
function mountController(stateOverrides = {}) {
|
||||||
|
const wrapper = mount(ControllerPage, {
|
||||||
|
global: {
|
||||||
|
stubs: { 'w-app': true, 'w-button': true }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (Object.keys(stateOverrides).length > 0) {
|
||||||
|
wrapper.vm.state = { ...wrapper.vm.state, ...stateOverrides }
|
||||||
|
}
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('ControllerPage.vue', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// RENDERING INIZIALE
|
||||||
|
// =============================================
|
||||||
|
describe('Rendering iniziale', () => {
|
||||||
|
it('dovrebbe mostrare i nomi dei team', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const text = wrapper.text()
|
||||||
|
expect(text).toContain('Antoniana')
|
||||||
|
expect(text).toContain('Guest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare punteggio 0-0', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const pts = wrapper.findAll('.team-pts')
|
||||||
|
expect(pts[0].text()).toBe('0')
|
||||||
|
expect(pts[1].text()).toBe('0')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare SET 0 per entrambi i team', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const sets = wrapper.findAll('.team-set')
|
||||||
|
expect(sets[0].text()).toContain('SET 0')
|
||||||
|
expect(sets[1].text()).toContain('SET 0')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// CLICK PUNTEGGIO
|
||||||
|
// =============================================
|
||||||
|
describe('Click punteggio', () => {
|
||||||
|
it('dovrebbe chiamare sendAction con incPunt home al click sul team home', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const spy = vi.spyOn(wrapper.vm, 'sendAction')
|
||||||
|
await wrapper.find('.team-score.home-bg').trigger('click')
|
||||||
|
expect(spy).toHaveBeenCalledWith({ type: 'incPunt', team: 'home' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe chiamare sendAction con incPunt guest al click sul team guest', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const spy = vi.spyOn(wrapper.vm, 'sendAction')
|
||||||
|
await wrapper.find('.team-score.guest-bg').trigger('click')
|
||||||
|
expect(spy).toHaveBeenCalledWith({ type: 'incPunt', team: 'guest' })
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// BOTTONE CAMBIO PALLA
|
||||||
|
// =============================================
|
||||||
|
describe('Cambio Palla', () => {
|
||||||
|
it('dovrebbe essere abilitato a 0-0', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const btn = wrapper.findAll('.btn-ctrl').find(b => b.text().includes('Cambio Palla'))
|
||||||
|
expect(btn.attributes('disabled')).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe essere disabilitato se il punteggio non è 0-0', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.state.sp.punt.home = 5
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const btn = wrapper.findAll('.btn-ctrl').find(b => b.text().includes('Cambio Palla'))
|
||||||
|
expect(btn.attributes('disabled')).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// DIALOG RESET
|
||||||
|
// =============================================
|
||||||
|
describe('Dialog Reset', () => {
|
||||||
|
it('click Reset dovrebbe aprire la conferma', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
expect(wrapper.find('.overlay').exists()).toBe(false)
|
||||||
|
await wrapper.find('.btn-danger').trigger('click')
|
||||||
|
expect(wrapper.vm.confirmReset).toBe(true)
|
||||||
|
expect(wrapper.find('.overlay').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('click NO dovrebbe chiudere la conferma', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.confirmReset = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
await wrapper.find('.btn-cancel').trigger('click')
|
||||||
|
expect(wrapper.vm.confirmReset).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('click SI dovrebbe chiamare doReset', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
const spy = vi.spyOn(wrapper.vm, 'sendAction')
|
||||||
|
wrapper.vm.confirmReset = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
await wrapper.find('.btn-confirm').trigger('click')
|
||||||
|
expect(spy).toHaveBeenCalledWith({ type: 'resetta' })
|
||||||
|
expect(wrapper.vm.confirmReset).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// COMPUTED cambiValid
|
||||||
|
// =============================================
|
||||||
|
describe('cambiValid', () => {
|
||||||
|
it('dovrebbe essere false se tutti i campi sono vuoti', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.cambiData = [{ in: '', out: '' }, { in: '', out: '' }]
|
||||||
|
expect(wrapper.vm.cambiValid).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe essere true con un cambio completo', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.cambiData = [{ in: '10', out: '1' }, { in: '', out: '' }]
|
||||||
|
expect(wrapper.vm.cambiValid).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe essere false con un cambio parziale (solo IN)', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.cambiData = [{ in: '10', out: '' }, { in: '', out: '' }]
|
||||||
|
expect(wrapper.vm.cambiValid).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe essere false con un cambio parziale (solo OUT)', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.cambiData = [{ in: '', out: '1' }, { in: '', out: '' }]
|
||||||
|
expect(wrapper.vm.cambiValid).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe essere true con due cambi completi', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.cambiData = [{ in: '10', out: '1' }, { in: '11', out: '2' }]
|
||||||
|
expect(wrapper.vm.cambiValid).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// SPEAK
|
||||||
|
// =============================================
|
||||||
|
describe('speak', () => {
|
||||||
|
it('dovrebbe generare "zero a zero" a 0-0', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
wrapper.vm.ws = { readyState: 1, send: vi.fn() }
|
||||||
|
wrapper.vm.speak()
|
||||||
|
const sent = JSON.parse(wrapper.vm.ws.send.mock.calls[0][0])
|
||||||
|
expect(sent.type).toBe('speak')
|
||||||
|
expect(sent.text).toBe('zero a zero')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe generare "N pari" a punteggio uguale', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.state.sp.punt.home = 5
|
||||||
|
wrapper.vm.state.sp.punt.guest = 5
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
wrapper.vm.ws = { readyState: 1, send: vi.fn() }
|
||||||
|
wrapper.vm.speak()
|
||||||
|
const sent = JSON.parse(wrapper.vm.ws.send.mock.calls[0][0])
|
||||||
|
expect(sent.text).toBe('5 pari')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe annunciare prima il punteggio di chi batte (home serve)', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.state.sp.punt.home = 15
|
||||||
|
wrapper.vm.state.sp.punt.guest = 10
|
||||||
|
wrapper.vm.state.sp.servHome = true
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
wrapper.vm.ws = { readyState: 1, send: vi.fn() }
|
||||||
|
wrapper.vm.speak()
|
||||||
|
const sent = JSON.parse(wrapper.vm.ws.send.mock.calls[0][0])
|
||||||
|
expect(sent.text).toBe('15 a 10')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe annunciare prima il punteggio di chi batte (guest serve)', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.state.sp.punt.home = 10
|
||||||
|
wrapper.vm.state.sp.punt.guest = 15
|
||||||
|
wrapper.vm.state.sp.servHome = false
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
wrapper.vm.ws = { readyState: 1, send: vi.fn() }
|
||||||
|
wrapper.vm.speak()
|
||||||
|
const sent = JSON.parse(wrapper.vm.ws.send.mock.calls[0][0])
|
||||||
|
expect(sent.text).toBe('15 a 10')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// BARRA CONNESSIONE
|
||||||
|
// =============================================
|
||||||
|
describe('Barra connessione', () => {
|
||||||
|
it('dovrebbe avere classe "connected" quando connesso', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.find('.conn-bar').classes()).toContain('connected')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe avere classe "connected" quando disconnesso', () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.wsConnected = false
|
||||||
|
expect(wrapper.find('.conn-bar').classes()).not.toContain('connected')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare "Connesso" quando connesso', async () => {
|
||||||
|
const wrapper = mountController()
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.find('.conn-bar').text()).toContain('Connesso')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
195
tests/component/DisplayPage.test.js
Normal file
195
tests/component/DisplayPage.test.js
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
// @vitest-environment happy-dom
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
||||||
|
import { mount } from '@vue/test-utils'
|
||||||
|
import DisplayPage from '../../src/components/DisplayPage.vue'
|
||||||
|
|
||||||
|
// Mock globale WebSocket per jsdom
|
||||||
|
class MockWebSocket {
|
||||||
|
static OPEN = 1
|
||||||
|
static CONNECTING = 0
|
||||||
|
readyState = 0
|
||||||
|
onopen = null
|
||||||
|
onclose = null
|
||||||
|
onmessage = null
|
||||||
|
onerror = null
|
||||||
|
send = vi.fn()
|
||||||
|
close = vi.fn()
|
||||||
|
constructor() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.readyState = 1
|
||||||
|
if (this.onopen) this.onopen()
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vi.stubGlobal('WebSocket', MockWebSocket)
|
||||||
|
|
||||||
|
// Mock requestFullscreen e speechSynthesis
|
||||||
|
vi.stubGlobal('speechSynthesis', {
|
||||||
|
speak: vi.fn(),
|
||||||
|
cancel: vi.fn(),
|
||||||
|
getVoices: () => []
|
||||||
|
})
|
||||||
|
|
||||||
|
function mountDisplay() {
|
||||||
|
return mount(DisplayPage, {
|
||||||
|
global: {
|
||||||
|
stubs: { 'w-app': true }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('DisplayPage.vue', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.useFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// RENDERING PUNTEGGIO
|
||||||
|
// =============================================
|
||||||
|
describe('Rendering punteggio', () => {
|
||||||
|
it('dovrebbe mostrare i nomi dei team', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const text = wrapper.text()
|
||||||
|
expect(text).toContain('Antoniana')
|
||||||
|
expect(text).toContain('Guest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare punteggio iniziale 0-0', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const punti = wrapper.findAll('.punt')
|
||||||
|
expect(punti[0].text()).toBe('0')
|
||||||
|
expect(punti[1].text()).toBe('0')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare i set corretti', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const text = wrapper.text()
|
||||||
|
expect(text).toContain('set 0')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe aggiornare il punteggio quando lo stato cambia', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.state.sp.punt.home = 15
|
||||||
|
wrapper.vm.state.sp.punt.guest = 12
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const punti = wrapper.findAll('.punt')
|
||||||
|
expect(punti[0].text()).toBe('15')
|
||||||
|
expect(punti[1].text()).toBe('12')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// ORDINE TEAM
|
||||||
|
// =============================================
|
||||||
|
describe('Ordine team', () => {
|
||||||
|
it('order=true → Home prima di Guest', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const headers = wrapper.findAll('.hea')
|
||||||
|
expect(headers[0].classes()).toContain('home')
|
||||||
|
expect(headers[1].classes()).toContain('guest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('order=false → Guest prima di Home', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.state.order = false
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const headers = wrapper.findAll('.hea')
|
||||||
|
expect(headers[0].classes()).toContain('guest')
|
||||||
|
expect(headers[1].classes()).toContain('home')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// FORMAZIONE vs PUNTEGGIO
|
||||||
|
// =============================================
|
||||||
|
describe('visuForm toggle', () => {
|
||||||
|
it('visuForm=false → mostra punteggio grande', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
expect(wrapper.find('.punteggio-container').exists()).toBe(true)
|
||||||
|
expect(wrapper.find('.form').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('visuForm=true → mostra formazione', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.state.visuForm = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.findAll('.form').length).toBeGreaterThan(0)
|
||||||
|
expect(wrapper.find('.punteggio-container').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('formazione mostra 6 giocatori per team', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.state.visuForm = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const formDivs = wrapper.findAll('.formdiv')
|
||||||
|
// 6 per home + 6 per guest = 12
|
||||||
|
expect(formDivs).toHaveLength(12)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// STRISCIA
|
||||||
|
// =============================================
|
||||||
|
describe('visuStriscia toggle', () => {
|
||||||
|
it('visuStriscia=true → mostra la striscia', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
expect(wrapper.find('.striscia').exists()).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('visuStriscia=false → nasconde la striscia', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.state.visuStriscia = false
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
expect(wrapper.find('.striscia').exists()).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// INDICATORE CONNESSIONE
|
||||||
|
// =============================================
|
||||||
|
describe('Indicatore connessione', () => {
|
||||||
|
it('dovrebbe avere classe "disconnected" quando non connesso', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const status = wrapper.find('.connection-status')
|
||||||
|
expect(status.classes()).toContain('disconnected')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere classe "connected" quando connesso', async () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
wrapper.vm.wsConnected = true
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
const status = wrapper.find('.connection-status')
|
||||||
|
expect(status.classes()).toContain('connected')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare "Disconnesso" quando non connesso', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
const status = wrapper.find('.connection-status')
|
||||||
|
expect(status.text()).toContain('Disconnesso')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// ICONA SERVIZIO
|
||||||
|
// =============================================
|
||||||
|
describe('Icona servizio', () => {
|
||||||
|
it('dovrebbe mostrare l\'icona servizio sul team home quando servHome=true', () => {
|
||||||
|
const wrapper = mountDisplay()
|
||||||
|
// v-show imposta display:none. In happy-dom controlliamo lo style.
|
||||||
|
const imgs = wrapper.findAll('.serv-slot img')
|
||||||
|
// Con state.order=true e servHome=true:
|
||||||
|
// - la prima img (home) è visibile (no display:none)
|
||||||
|
// - la seconda img (guest) ha display:none
|
||||||
|
const homeStyle = imgs[0].attributes('style') || ''
|
||||||
|
const guestStyle = imgs[1].attributes('style') || ''
|
||||||
|
expect(homeStyle).not.toContain('display: none')
|
||||||
|
expect(guestStyle).toContain('display: none')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -16,68 +16,388 @@ class MockWebSocketServer extends EventEmitter {
|
|||||||
clients = new Set()
|
clients = new Set()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper: connette e registra un client
|
||||||
|
function connectAndRegister(wss, role) {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
ws.emit('message', JSON.stringify({ type: 'register', role }))
|
||||||
|
ws.send.mockClear()
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper: ultimo messaggio inviato a un ws
|
||||||
|
function lastSent(ws) {
|
||||||
|
const calls = ws.send.mock.calls
|
||||||
|
return JSON.parse(calls[calls.length - 1][0])
|
||||||
|
}
|
||||||
|
|
||||||
describe('WebSocket Integration (websocket-handler.js)', () => {
|
describe('WebSocket Integration (websocket-handler.js)', () => {
|
||||||
let wss
|
let wss
|
||||||
let handler
|
let handler
|
||||||
let ws
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wss = new MockWebSocketServer()
|
wss = new MockWebSocketServer()
|
||||||
handler = setupWebSocketHandler(wss)
|
handler = setupWebSocketHandler(wss)
|
||||||
ws = new MockWebSocket()
|
|
||||||
// Simuliamo la connessione
|
|
||||||
wss.emit('connection', ws)
|
|
||||||
// Aggiungiamo il client al set del server (come farebbe 'ws' realmente)
|
|
||||||
wss.clients.add(ws)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
vi.restoreAllMocks()
|
vi.restoreAllMocks()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe registrare un client come "display" e inviare lo stato', () => {
|
// =============================================
|
||||||
ws.emit('message', JSON.stringify({ type: 'register', role: 'display' }))
|
// REGISTRAZIONE
|
||||||
|
// =============================================
|
||||||
|
describe('Registrazione', () => {
|
||||||
|
it('dovrebbe registrare un client come "display" e inviare lo stato', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
ws.emit('message', JSON.stringify({ type: 'register', role: 'display' }))
|
||||||
|
|
||||||
// Verifica che abbia inviato lo stato iniziale
|
expect(ws.send).toHaveBeenCalled()
|
||||||
expect(ws.send).toHaveBeenCalled()
|
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
||||||
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
expect(sentMsg.type).toBe('state')
|
||||||
expect(sentMsg.type).toBe('state')
|
expect(sentMsg.state).toBeDefined()
|
||||||
expect(sentMsg.state).toBeDefined()
|
})
|
||||||
|
|
||||||
|
it('dovrebbe registrare un client come "controller"', () => {
|
||||||
|
connectAndRegister(wss, 'controller')
|
||||||
|
expect(handler.getClients().size).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe rifiutare ruolo non valido', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
ws.emit('message', JSON.stringify({ type: 'register', role: 'hacker' }))
|
||||||
|
|
||||||
|
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
||||||
|
expect(sentMsg.type).toBe('error')
|
||||||
|
expect(sentMsg.message).toContain('Invalid role')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe usare "display" come ruolo default se mancante', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
ws.emit('message', JSON.stringify({ type: 'register' }))
|
||||||
|
|
||||||
|
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
||||||
|
expect(sentMsg.type).toBe('state')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe permettere al controller di cambiare il punteggio', () => {
|
// =============================================
|
||||||
// 1. Registra Controller
|
// AZIONI
|
||||||
ws.emit('message', JSON.stringify({ type: 'register', role: 'controller' }))
|
// =============================================
|
||||||
ws.send.mockClear() // pulisco chiamate precedenti
|
describe('Azioni', () => {
|
||||||
|
it('dovrebbe permettere al controller di cambiare il punteggio', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
// 2. Invia Azione
|
controller.emit('message', JSON.stringify({
|
||||||
ws.emit('message', JSON.stringify({
|
type: 'action',
|
||||||
type: 'action',
|
action: { type: 'incPunt', team: 'home' }
|
||||||
action: { type: 'incPunt', team: 'home' }
|
}))
|
||||||
}))
|
|
||||||
|
|
||||||
// 3. Verifica Broadcast del nuovo stato
|
expect(controller.send).toHaveBeenCalled()
|
||||||
expect(ws.send).toHaveBeenCalled()
|
const sentMsg = lastSent(controller)
|
||||||
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
expect(sentMsg.type).toBe('state')
|
||||||
expect(sentMsg.type).toBe('state')
|
expect(sentMsg.state.sp.punt.home).toBe(1)
|
||||||
expect(sentMsg.state.sp.punt.home).toBe(1)
|
})
|
||||||
|
|
||||||
|
it('dovrebbe impedire al display di inviare azioni', () => {
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
|
||||||
|
display.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sentMsg = lastSent(display)
|
||||||
|
expect(sentMsg.type).toBe('error')
|
||||||
|
expect(sentMsg.message).toContain('Only controllers')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe impedire azioni da client non registrati', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
|
||||||
|
ws.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
||||||
|
expect(sentMsg.type).toBe('error')
|
||||||
|
expect(sentMsg.message).toContain('Only controllers')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe rifiutare azione con formato invalido (missing action)', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sentMsg = lastSent(controller)
|
||||||
|
expect(sentMsg.type).toBe('error')
|
||||||
|
expect(sentMsg.message).toContain('Invalid action format')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe rifiutare azione con formato invalido (missing action.type)', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sentMsg = lastSent(controller)
|
||||||
|
expect(sentMsg.type).toBe('error')
|
||||||
|
expect(sentMsg.message).toContain('Invalid action format')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe impedire al display di inviare azioni', () => {
|
// =============================================
|
||||||
// 1. Registra Display
|
// BROADCAST MULTI-CLIENT
|
||||||
ws.emit('message', JSON.stringify({ type: 'register', role: 'display' }))
|
// =============================================
|
||||||
ws.send.mockClear()
|
describe('Broadcast', () => {
|
||||||
|
it('dovrebbe inviare lo stato a tutti i client dopo un\'azione', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
const display1 = connectAndRegister(wss, 'display')
|
||||||
|
const display2 = connectAndRegister(wss, 'display')
|
||||||
|
|
||||||
// 2. Tenta Azione
|
controller.emit('message', JSON.stringify({
|
||||||
ws.emit('message', JSON.stringify({
|
type: 'action',
|
||||||
type: 'action',
|
action: { type: 'incPunt', team: 'home' }
|
||||||
action: { type: 'incPunt', team: 'home' }
|
}))
|
||||||
}))
|
|
||||||
|
|
||||||
// 3. Verifica Errore
|
// Tutti i client nel set dovrebbero aver ricevuto lo stato
|
||||||
expect(ws.send).toHaveBeenCalled()
|
expect(controller.send).toHaveBeenCalled()
|
||||||
const sentMsg = JSON.parse(ws.send.mock.calls[0][0])
|
expect(display1.send).toHaveBeenCalled()
|
||||||
expect(sentMsg.type).toBe('error')
|
expect(display2.send).toHaveBeenCalled()
|
||||||
expect(sentMsg.message).toContain('Only controllers')
|
|
||||||
|
const msg1 = lastSent(display1)
|
||||||
|
const msg2 = lastSent(display2)
|
||||||
|
expect(msg1.type).toBe('state')
|
||||||
|
expect(msg1.state.sp.punt.home).toBe(1)
|
||||||
|
expect(msg2.state.sp.punt.home).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe inviare a client con readyState != OPEN', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
const closedClient = connectAndRegister(wss, 'display')
|
||||||
|
closedClient.readyState = 3 // CLOSED
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
// closedClient non dovrebbe aver ricevuto il broadcast
|
||||||
|
expect(closedClient.send).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// SPEAK
|
||||||
|
// =============================================
|
||||||
|
describe('Speak', () => {
|
||||||
|
it('dovrebbe inoltrare il messaggio speak solo ai display', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'speak',
|
||||||
|
text: 'quindici a dieci'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Il display riceve il messaggio speak
|
||||||
|
expect(display.send).toHaveBeenCalled()
|
||||||
|
const msg = lastSent(display)
|
||||||
|
expect(msg.type).toBe('speak')
|
||||||
|
expect(msg.text).toBe('quindici a dieci')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe permettere al display di inviare speak', () => {
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
|
||||||
|
display.emit('message', JSON.stringify({
|
||||||
|
type: 'speak',
|
||||||
|
text: 'test'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const msg = lastSent(display)
|
||||||
|
expect(msg.type).toBe('error')
|
||||||
|
expect(msg.message).toContain('Only controllers')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe rifiutare speak con testo vuoto', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'speak',
|
||||||
|
text: ' '
|
||||||
|
}))
|
||||||
|
|
||||||
|
const msg = lastSent(controller)
|
||||||
|
expect(msg.type).toBe('error')
|
||||||
|
expect(msg.message).toContain('Invalid speak payload')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe rifiutare speak senza testo', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'speak'
|
||||||
|
}))
|
||||||
|
|
||||||
|
const msg = lastSent(controller)
|
||||||
|
expect(msg.type).toBe('error')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe fare trim del testo speak', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'speak',
|
||||||
|
text: ' dieci a otto '
|
||||||
|
}))
|
||||||
|
|
||||||
|
const msg = lastSent(display)
|
||||||
|
expect(msg.text).toBe('dieci a otto')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// MESSAGGI MALFORMATI
|
||||||
|
// =============================================
|
||||||
|
describe('Messaggi malformati', () => {
|
||||||
|
it('dovrebbe gestire JSON non valido senza crash', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
ws.emit('message', 'questo non è JSON {{{')
|
||||||
|
}).not.toThrow()
|
||||||
|
|
||||||
|
const msg = lastSent(ws)
|
||||||
|
expect(msg.type).toBe('error')
|
||||||
|
expect(msg.message).toContain('Invalid message format')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire Buffer come input', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
const buf = Buffer.from(JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
controller.emit('message', buf)
|
||||||
|
|
||||||
|
const msg = lastSent(controller)
|
||||||
|
expect(msg.type).toBe('state')
|
||||||
|
expect(msg.state.sp.punt.home).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// DISCONNESSIONE
|
||||||
|
// =============================================
|
||||||
|
describe('Disconnessione', () => {
|
||||||
|
it('dovrebbe rimuovere il client dalla mappa alla disconnessione', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
expect(handler.getClients().size).toBe(1)
|
||||||
|
|
||||||
|
controller.emit('close')
|
||||||
|
|
||||||
|
expect(handler.getClients().size).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('i client rimanenti non dovrebbero essere affetti dalla disconnessione', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
expect(handler.getClients().size).toBe(2)
|
||||||
|
|
||||||
|
controller.emit('close')
|
||||||
|
expect(handler.getClients().size).toBe(1)
|
||||||
|
|
||||||
|
expect(handler.getClients().has(display)).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// ERRORI WEBSOCKET
|
||||||
|
// =============================================
|
||||||
|
describe('Errori WebSocket', () => {
|
||||||
|
it('dovrebbe terminare la connessione per errore UTF8 invalido', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
|
||||||
|
const err = new Error('Invalid UTF8')
|
||||||
|
err.code = 'WS_ERR_INVALID_UTF8'
|
||||||
|
ws.emit('error', err)
|
||||||
|
|
||||||
|
expect(ws.terminate).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe terminare la connessione per close code invalido', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
|
||||||
|
const err = new Error('Invalid close code')
|
||||||
|
err.code = 'WS_ERR_INVALID_CLOSE_CODE'
|
||||||
|
ws.emit('error', err)
|
||||||
|
|
||||||
|
expect(ws.terminate).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe terminare per altri errori', () => {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
|
||||||
|
const err = new Error('Generic error')
|
||||||
|
ws.emit('error', err)
|
||||||
|
|
||||||
|
expect(ws.terminate).not.toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// API PUBBLICA
|
||||||
|
// =============================================
|
||||||
|
describe('API pubblica', () => {
|
||||||
|
it('getState dovrebbe restituire lo stato corrente', () => {
|
||||||
|
const state = handler.getState()
|
||||||
|
expect(state.sp.punt.home).toBe(0)
|
||||||
|
expect(state.sp.punt.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('setState dovrebbe sovrascrivere lo stato', () => {
|
||||||
|
const newState = handler.getState()
|
||||||
|
newState.sp.punt.home = 99
|
||||||
|
handler.setState(newState)
|
||||||
|
expect(handler.getState().sp.punt.home).toBe(99)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('broadcastState dovrebbe inviare a tutti i client', () => {
|
||||||
|
const display = connectAndRegister(wss, 'display')
|
||||||
|
handler.broadcastState()
|
||||||
|
expect(display.send).toHaveBeenCalled()
|
||||||
|
const msg = lastSent(display)
|
||||||
|
expect(msg.type).toBe('state')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('getClients dovrebbe restituire la mappa dei client', () => {
|
||||||
|
expect(handler.getClients()).toBeInstanceOf(Map)
|
||||||
|
expect(handler.getClients().size).toBe(0)
|
||||||
|
connectAndRegister(wss, 'display')
|
||||||
|
expect(handler.getClients().size).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
118
tests/stress/websocket-load.test.js
Normal file
118
tests/stress/websocket-load.test.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
|
||||||
|
import { setupWebSocketHandler } from '../../src/websocket-handler.js'
|
||||||
|
import { EventEmitter } from 'events'
|
||||||
|
|
||||||
|
class MockWebSocket extends EventEmitter {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
this.readyState = 1
|
||||||
|
}
|
||||||
|
send = vi.fn()
|
||||||
|
terminate = vi.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockWebSocketServer extends EventEmitter {
|
||||||
|
clients = new Set()
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectAndRegister(wss, role) {
|
||||||
|
const ws = new MockWebSocket()
|
||||||
|
wss.emit('connection', ws)
|
||||||
|
wss.clients.add(ws)
|
||||||
|
ws.emit('message', JSON.stringify({ type: 'register', role }))
|
||||||
|
ws.send.mockClear()
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Stress Test WebSocket', () => {
|
||||||
|
let wss
|
||||||
|
let handler
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
wss = new MockWebSocketServer()
|
||||||
|
handler = setupWebSocketHandler(wss)
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire 50 client display connessi simultaneamente', () => {
|
||||||
|
const displays = []
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
displays.push(connectAndRegister(wss, 'display'))
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(handler.getClients().size).toBe(50)
|
||||||
|
|
||||||
|
// Un controller invia un'azione
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Tutti i display devono aver ricevuto il broadcast
|
||||||
|
for (const display of displays) {
|
||||||
|
expect(display.send).toHaveBeenCalled()
|
||||||
|
const msg = JSON.parse(display.send.mock.calls[display.send.mock.calls.length - 1][0])
|
||||||
|
expect(msg.type).toBe('state')
|
||||||
|
expect(msg.state.sp.punt.home).toBe(1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire 100 azioni rapide in sequenza con stato finale corretto', () => {
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
// 60 punti home, 40 punti guest
|
||||||
|
for (let i = 0; i < 60; i++) {
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
for (let i = 0; i < 40; i++) {
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'guest' }
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lo stato finale dipende da checkVittoria che blocca a 25+2
|
||||||
|
// Home arriva a 25-0 → vittoria → blocca. Quindi punti home = 25
|
||||||
|
const state = handler.getState()
|
||||||
|
expect(state.sp.punt.home).toBe(25)
|
||||||
|
// Guest: non può segnare dopo vittoria? No, checkVittoria blocca solo il team che ha vinto?
|
||||||
|
// Controlliamo: checkVittoria controlla ENTRAMBI i team.
|
||||||
|
// A 25-0 → vittoria=true → incPunt per guest è anche bloccato
|
||||||
|
expect(state.sp.punt.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe garantire che tutti i display ricevano ogni update sotto carico', () => {
|
||||||
|
const displays = []
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
displays.push(connectAndRegister(wss, 'display'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const controller = connectAndRegister(wss, 'controller')
|
||||||
|
|
||||||
|
// 5 azioni rapide
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
controller.emit('message', JSON.stringify({
|
||||||
|
type: 'action',
|
||||||
|
action: { type: 'incPunt', team: 'home' }
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ogni display deve aver ricevuto esattamente 5 broadcast
|
||||||
|
for (const display of displays) {
|
||||||
|
expect(display.send).toHaveBeenCalledTimes(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verifica stato finale su tutti i display
|
||||||
|
for (const display of displays) {
|
||||||
|
const lastMsg = JSON.parse(display.send.mock.calls[4][0])
|
||||||
|
expect(lastMsg.state.sp.punt.home).toBe(5)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -8,7 +8,10 @@ describe('Game Logic (gameState.js)', () => {
|
|||||||
state = createInitialState()
|
state = createInitialState()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Initial State', () => {
|
// =============================================
|
||||||
|
// STATO INIZIALE
|
||||||
|
// =============================================
|
||||||
|
describe('Stato iniziale', () => {
|
||||||
it('dovrebbe iniziare con 0-0', () => {
|
it('dovrebbe iniziare con 0-0', () => {
|
||||||
expect(state.sp.punt.home).toBe(0)
|
expect(state.sp.punt.home).toBe(0)
|
||||||
expect(state.sp.punt.guest).toBe(0)
|
expect(state.sp.punt.guest).toBe(0)
|
||||||
@@ -18,40 +21,451 @@ describe('Game Logic (gameState.js)', () => {
|
|||||||
expect(state.sp.set.home).toBe(0)
|
expect(state.sp.set.home).toBe(0)
|
||||||
expect(state.sp.set.guest).toBe(0)
|
expect(state.sp.set.guest).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere servizio Home', () => {
|
||||||
|
expect(state.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere formazione di default [1-6]', () => {
|
||||||
|
expect(state.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
expect(state.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere la striscia iniziale a [0]', () => {
|
||||||
|
expect(state.sp.striscia.home).toEqual([0])
|
||||||
|
expect(state.sp.striscia.guest).toEqual([0])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere storico servizio vuoto', () => {
|
||||||
|
expect(state.sp.storicoServizio).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere modalità 3/5 di default', () => {
|
||||||
|
expect(state.modalitaPartita).toBe("3/5")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe avere visuForm false e visuStriscia true', () => {
|
||||||
|
expect(state.visuForm).toBe(false)
|
||||||
|
expect(state.visuStriscia).toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Punteggio', () => {
|
// =============================================
|
||||||
it('dovrebbe incrementare i punti (Home)', () => {
|
// IMMUTABILITÀ
|
||||||
|
// =============================================
|
||||||
|
describe('Immutabilità', () => {
|
||||||
|
it('applyAction non dovrebbe mutare lo stato originale', () => {
|
||||||
|
const original = JSON.stringify(state)
|
||||||
|
applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(JSON.stringify(state)).toBe(original)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe restituire un nuovo oggetto', () => {
|
||||||
|
const newState = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(newState).not.toBe(state)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// INCREMENTO PUNTI (incPunt)
|
||||||
|
// =============================================
|
||||||
|
describe('incPunt', () => {
|
||||||
|
it('dovrebbe incrementare i punti Home', () => {
|
||||||
const newState = applyAction(state, { type: 'incPunt', team: 'home' })
|
const newState = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
expect(newState.sp.punt.home).toBe(1)
|
expect(newState.sp.punt.home).toBe(1)
|
||||||
expect(newState.sp.punt.guest).toBe(0)
|
expect(newState.sp.punt.guest).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe gestire il cambio palla', () => {
|
it('dovrebbe incrementare i punti Guest', () => {
|
||||||
// Home batte
|
const newState = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
state.sp.servHome = true
|
expect(newState.sp.punt.guest).toBe(1)
|
||||||
// Punto Guest -> Cambio palla
|
expect(newState.sp.punt.home).toBe(0)
|
||||||
const s1 = applyAction(state, { type: 'incPunt', team: 'guest' })
|
|
||||||
expect(s1.sp.servHome).toBe(false) // Ora batte Guest
|
|
||||||
|
|
||||||
// Punto Home -> Cambio palla
|
|
||||||
const s2 = applyAction(s1, { type: 'incPunt', team: 'home' })
|
|
||||||
expect(s2.sp.servHome).toBe(true) // Torna a battere Home
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe gestire la rotazione formazione al cambio palla', () => {
|
it('dovrebbe gestire il cambio palla (Guest segna, batteva Home)', () => {
|
||||||
state.sp.servHome = true // Batte Home
|
state.sp.servHome = true
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
expect(s1.sp.servHome).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire il cambio palla (Home segna, batteva Guest)', () => {
|
||||||
|
state.sp.servHome = false
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s1.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe cambiare palla se segna chi batte', () => {
|
||||||
|
state.sp.servHome = true
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s1.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe ruotare la formazione al cambio palla', () => {
|
||||||
|
state.sp.servHome = true
|
||||||
state.sp.form.guest = ["1", "2", "3", "4", "5", "6"]
|
state.sp.form.guest = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
|
||||||
// Punto Guest -> Cambio palla e rotazione Guest
|
|
||||||
const newState = applyAction(state, { type: 'incPunt', team: 'guest' })
|
const newState = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
|
||||||
// Verifica che la formazione sia ruotata (il primo elemento diventa ultimo)
|
|
||||||
expect(newState.sp.form.guest).toEqual(["2", "3", "4", "5", "6", "1"])
|
expect(newState.sp.form.guest).toEqual(["2", "3", "4", "5", "6", "1"])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe ruotare la formazione se non c\'è cambio palla', () => {
|
||||||
|
state.sp.servHome = true
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const newState = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(newState.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe aggiornare la striscia per punto Home', () => {
|
||||||
|
const s = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s.sp.striscia.home).toEqual([0, 1])
|
||||||
|
expect(s.sp.striscia.guest).toEqual([0, " "])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe aggiornare la striscia per punto Guest', () => {
|
||||||
|
const s = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
expect(s.sp.striscia.guest).toEqual([0, 1])
|
||||||
|
expect(s.sp.striscia.home).toEqual([0, " "])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe registrare lo storico servizio', () => {
|
||||||
|
const s = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s.sp.storicoServizio).toHaveLength(1)
|
||||||
|
expect(s.sp.storicoServizio[0]).toHaveProperty('servHome')
|
||||||
|
expect(s.sp.storicoServizio[0]).toHaveProperty('cambioPalla')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe incrementare i punti dopo vittoria', () => {
|
||||||
|
state.sp.punt.home = 25
|
||||||
|
state.sp.punt.guest = 23
|
||||||
|
const s = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s.sp.punt.home).toBe(25)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Vittoria Set', () => {
|
// =============================================
|
||||||
|
// DECREMENTO PUNTI (decPunt)
|
||||||
|
// =============================================
|
||||||
|
describe('decPunt', () => {
|
||||||
|
it('dovrebbe annullare l\'ultimo punto Home', () => {
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
const s2 = applyAction(s1, { type: 'decPunt' })
|
||||||
|
expect(s2.sp.punt.home).toBe(0)
|
||||||
|
expect(s2.sp.punt.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe annullare l\'ultimo punto Guest', () => {
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
const s2 = applyAction(s1, { type: 'decPunt' })
|
||||||
|
expect(s2.sp.punt.home).toBe(0)
|
||||||
|
expect(s2.sp.punt.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe fare nulla sullo stato iniziale', () => {
|
||||||
|
const s = applyAction(state, { type: 'decPunt' })
|
||||||
|
expect(s.sp.punt.home).toBe(0)
|
||||||
|
expect(s.sp.punt.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe ripristinare il servizio dopo undo con cambio palla', () => {
|
||||||
|
state.sp.servHome = true
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
expect(s1.sp.servHome).toBe(false)
|
||||||
|
const s2 = applyAction(s1, { type: 'decPunt' })
|
||||||
|
expect(s2.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe invertire la rotazione dopo undo con cambio palla', () => {
|
||||||
|
state.sp.servHome = true
|
||||||
|
state.sp.form.guest = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'guest' })
|
||||||
|
expect(s1.sp.form.guest).toEqual(["2", "3", "4", "5", "6", "1"])
|
||||||
|
const s2 = applyAction(s1, { type: 'decPunt' })
|
||||||
|
expect(s2.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe ripristinare la striscia', () => {
|
||||||
|
const s1 = applyAction(state, { type: 'incPunt', team: 'home' })
|
||||||
|
const s2 = applyAction(s1, { type: 'decPunt' })
|
||||||
|
expect(s2.sp.striscia.home).toEqual([0])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire undo multipli in sequenza', () => {
|
||||||
|
let s = state
|
||||||
|
s = applyAction(s, { type: 'incPunt', team: 'home' })
|
||||||
|
s = applyAction(s, { type: 'incPunt', team: 'guest' })
|
||||||
|
s = applyAction(s, { type: 'incPunt', team: 'home' })
|
||||||
|
expect(s.sp.punt.home).toBe(2)
|
||||||
|
expect(s.sp.punt.guest).toBe(1)
|
||||||
|
s = applyAction(s, { type: 'decPunt' })
|
||||||
|
expect(s.sp.punt.home).toBe(1)
|
||||||
|
s = applyAction(s, { type: 'decPunt' })
|
||||||
|
expect(s.sp.punt.guest).toBe(0)
|
||||||
|
s = applyAction(s, { type: 'decPunt' })
|
||||||
|
expect(s.sp.punt.home).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// INCREMENTO SET (incSet)
|
||||||
|
// =============================================
|
||||||
|
describe('incSet', () => {
|
||||||
|
it('dovrebbe incrementare il set Home', () => {
|
||||||
|
const s = applyAction(state, { type: 'incSet', team: 'home' })
|
||||||
|
expect(s.sp.set.home).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe incrementare il set Guest', () => {
|
||||||
|
const s = applyAction(state, { type: 'incSet', team: 'guest' })
|
||||||
|
expect(s.sp.set.guest).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe fare wrap da 2 a 0', () => {
|
||||||
|
state.sp.set.home = 2
|
||||||
|
const s = applyAction(state, { type: 'incSet', team: 'home' })
|
||||||
|
expect(s.sp.set.home).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe incrementare da 1 a 2', () => {
|
||||||
|
state.sp.set.home = 1
|
||||||
|
const s = applyAction(state, { type: 'incSet', team: 'home' })
|
||||||
|
expect(s.sp.set.home).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// CAMBIO PALLA (cambiaPalla)
|
||||||
|
// =============================================
|
||||||
|
describe('cambiaPalla', () => {
|
||||||
|
it('dovrebbe invertire il servizio a 0-0', () => {
|
||||||
|
expect(state.sp.servHome).toBe(true)
|
||||||
|
const s = applyAction(state, { type: 'cambiaPalla' })
|
||||||
|
expect(s.sp.servHome).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe tornare a Home con doppio toggle', () => {
|
||||||
|
let s = applyAction(state, { type: 'cambiaPalla' })
|
||||||
|
s = applyAction(s, { type: 'cambiaPalla' })
|
||||||
|
expect(s.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe cambiare palla se il punteggio non è 0-0', () => {
|
||||||
|
state.sp.punt.home = 1
|
||||||
|
const s = applyAction(state, { type: 'cambiaPalla' })
|
||||||
|
expect(s.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe cambiare palla se Guest ha punti', () => {
|
||||||
|
state.sp.punt.guest = 3
|
||||||
|
const s = applyAction(state, { type: 'cambiaPalla' })
|
||||||
|
expect(s.sp.servHome).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// TOGGLE (toggleFormazione, toggleStriscia, toggleOrder)
|
||||||
|
// =============================================
|
||||||
|
describe('Toggle', () => {
|
||||||
|
it('toggleFormazione: false → true', () => {
|
||||||
|
expect(state.visuForm).toBe(false)
|
||||||
|
const s = applyAction(state, { type: 'toggleFormazione' })
|
||||||
|
expect(s.visuForm).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toggleFormazione: true → false', () => {
|
||||||
|
state.visuForm = true
|
||||||
|
const s = applyAction(state, { type: 'toggleFormazione' })
|
||||||
|
expect(s.visuForm).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toggleStriscia: true → false', () => {
|
||||||
|
expect(state.visuStriscia).toBe(true)
|
||||||
|
const s = applyAction(state, { type: 'toggleStriscia' })
|
||||||
|
expect(s.visuStriscia).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toggleOrder: true → false', () => {
|
||||||
|
expect(state.order).toBe(true)
|
||||||
|
const s = applyAction(state, { type: 'toggleOrder' })
|
||||||
|
expect(s.order).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// NOMI (setNomi)
|
||||||
|
// =============================================
|
||||||
|
describe('setNomi', () => {
|
||||||
|
it('dovrebbe aggiornare entrambi i nomi', () => {
|
||||||
|
const s = applyAction(state, { type: 'setNomi', home: 'Volley A', guest: 'Volley B' })
|
||||||
|
expect(s.sp.nomi.home).toBe('Volley A')
|
||||||
|
expect(s.sp.nomi.guest).toBe('Volley B')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe aggiornare solo il nome Home se guest è undefined', () => {
|
||||||
|
const s = applyAction(state, { type: 'setNomi', home: 'Volley A' })
|
||||||
|
expect(s.sp.nomi.home).toBe('Volley A')
|
||||||
|
expect(s.sp.nomi.guest).toBe('Guest')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe aggiornare solo il nome Guest se home è undefined', () => {
|
||||||
|
const s = applyAction(state, { type: 'setNomi', guest: 'Volley B' })
|
||||||
|
expect(s.sp.nomi.home).toBe('Antoniana')
|
||||||
|
expect(s.sp.nomi.guest).toBe('Volley B')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// MODALITÀ (setModalita)
|
||||||
|
// =============================================
|
||||||
|
describe('setModalita', () => {
|
||||||
|
it('dovrebbe cambiare in 2/3', () => {
|
||||||
|
const s = applyAction(state, { type: 'setModalita', modalita: '2/3' })
|
||||||
|
expect(s.modalitaPartita).toBe('2/3')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe cambiare in 3/5', () => {
|
||||||
|
state.modalitaPartita = '2/3'
|
||||||
|
const s = applyAction(state, { type: 'setModalita', modalita: '3/5' })
|
||||||
|
expect(s.modalitaPartita).toBe('3/5')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// FORMAZIONE (setFormazione)
|
||||||
|
// =============================================
|
||||||
|
describe('setFormazione', () => {
|
||||||
|
it('dovrebbe sostituire la formazione Home', () => {
|
||||||
|
const nuova = ["10", "11", "12", "13", "14", "15"]
|
||||||
|
const s = applyAction(state, { type: 'setFormazione', team: 'home', form: nuova })
|
||||||
|
expect(s.sp.form.home).toEqual(nuova)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe sostituire la formazione Guest', () => {
|
||||||
|
const nuova = ["7", "8", "9", "10", "11", "12"]
|
||||||
|
const s = applyAction(state, { type: 'setFormazione', team: 'guest', form: nuova })
|
||||||
|
expect(s.sp.form.guest).toEqual(nuova)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe modificare se manca team', () => {
|
||||||
|
const s = applyAction(state, { type: 'setFormazione', form: ["7", "8", "9", "10", "11", "12"] })
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe modificare se manca form', () => {
|
||||||
|
const s = applyAction(state, { type: 'setFormazione', team: 'home' })
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// CAMBI GIOCATORI (confermaCambi)
|
||||||
|
// =============================================
|
||||||
|
describe('confermaCambi', () => {
|
||||||
|
it('dovrebbe effettuare una sostituzione valida', () => {
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "10", out: "3" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toContain("10")
|
||||||
|
expect(s.sp.form.home).not.toContain("3")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire doppia sostituzione', () => {
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [
|
||||||
|
{ in: "10", out: "1" },
|
||||||
|
{ in: "11", out: "2" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toContain("10")
|
||||||
|
expect(s.sp.form.home).toContain("11")
|
||||||
|
expect(s.sp.form.home).not.toContain("1")
|
||||||
|
expect(s.sp.form.home).not.toContain("2")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe accettare input non numerico', () => {
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "abc", out: "1" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe accettare in == out', () => {
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "1", out: "1" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe accettare giocatore IN già in formazione', () => {
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "2", out: "1" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe accettare giocatore OUT non in formazione', () => {
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "10", out: "99" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe saltare cambi con campo vuoto', () => {
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [
|
||||||
|
{ in: "", out: "" },
|
||||||
|
{ in: "10", out: "1" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toContain("10")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mantenere la posizione del giocatore sostituito', () => {
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [{ in: "10", out: "3" }]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home[2]).toBe("10")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe gestire cambi sequenziali che dipendono l\'uno dall\'altro', () => {
|
||||||
|
// Sostituisci 1→10, poi 10→20 (il secondo dipende dal risultato del primo)
|
||||||
|
state.sp.form.home = ["1", "2", "3", "4", "5", "6"]
|
||||||
|
const s = applyAction(state, {
|
||||||
|
type: 'confermaCambi',
|
||||||
|
team: 'home',
|
||||||
|
cambi: [
|
||||||
|
{ in: "10", out: "1" },
|
||||||
|
{ in: "20", out: "10" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(s.sp.form.home).toContain("20")
|
||||||
|
expect(s.sp.form.home).not.toContain("1")
|
||||||
|
expect(s.sp.form.home).not.toContain("10")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// VITTORIA SET (checkVittoria)
|
||||||
|
// =============================================
|
||||||
|
describe('checkVittoria', () => {
|
||||||
it('non dovrebbe dare vittoria a 24-24', () => {
|
it('non dovrebbe dare vittoria a 24-24', () => {
|
||||||
state.sp.punt.home = 24
|
state.sp.punt.home = 24
|
||||||
state.sp.punt.guest = 24
|
state.sp.punt.guest = 24
|
||||||
@@ -64,28 +478,182 @@ describe('Game Logic (gameState.js)', () => {
|
|||||||
expect(checkVittoria(state)).toBe(true)
|
expect(checkVittoria(state)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('dovrebbe richiedere 2 punti di scarto (26-24)', () => {
|
it('non dovrebbe dare vittoria a 25-24 (serve 2 punti di scarto)', () => {
|
||||||
state.sp.punt.home = 25
|
state.sp.punt.home = 25
|
||||||
state.sp.punt.guest = 24
|
state.sp.punt.guest = 24
|
||||||
expect(checkVittoria(state)).toBe(false)
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe dare vittoria a 26-24', () => {
|
||||||
state.sp.punt.home = 26
|
state.sp.punt.home = 26
|
||||||
|
state.sp.punt.guest = 24
|
||||||
expect(checkVittoria(state)).toBe(true)
|
expect(checkVittoria(state)).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('dovrebbe dare vittoria Guest a 25-20', () => {
|
||||||
|
state.sp.punt.home = 20
|
||||||
|
state.sp.punt.guest = 25
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe dare vittoria ai vantaggi (30-28)', () => {
|
||||||
|
state.sp.punt.home = 30
|
||||||
|
state.sp.punt.guest = 28
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe dare vittoria ai vantaggi senza scarto (28-27)', () => {
|
||||||
|
state.sp.punt.home = 28
|
||||||
|
state.sp.punt.guest = 27
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Reset', () => {
|
// =============================================
|
||||||
it('dovrebbe resettare tutto a zero', () => {
|
// SET DECISIVO (15 punti)
|
||||||
state.sp.punt.home = 10
|
// =============================================
|
||||||
|
describe('Set decisivo', () => {
|
||||||
|
it('modalità 3/5: set decisivo dopo 4 set totali → vittoria a 15', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 2
|
||||||
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 3/5: non vittoria a 14-10 nel set decisivo', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 2
|
||||||
|
state.sp.punt.home = 14
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 3/5: vittoria a 15-13 nel set decisivo', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 2
|
||||||
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 13
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 3/5: non vittoria a 15-14 nel set decisivo (serve scarto)', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 2
|
||||||
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 14
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 3/5: vittoria a 16-14 nel set decisivo', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 2
|
||||||
|
state.sp.punt.home = 16
|
||||||
|
state.sp.punt.guest = 14
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 2/3: set decisivo dopo 2 set totali → vittoria a 15', () => {
|
||||||
|
state.modalitaPartita = "2/3"
|
||||||
state.sp.set.home = 1
|
state.sp.set.home = 1
|
||||||
|
state.sp.set.guest = 1
|
||||||
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
const newState = applyAction(state, { type: 'resetta' })
|
it('modalità 2/3: non vittoria a 24-20 nel set decisivo (soglia 15)', () => {
|
||||||
|
state.modalitaPartita = "2/3"
|
||||||
|
state.sp.set.home = 1
|
||||||
|
state.sp.set.guest = 1
|
||||||
|
state.sp.punt.home = 14
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
expect(newState.sp.punt.home).toBe(0)
|
it('modalità 2/3: set non decisivo (1-0) → soglia 25', () => {
|
||||||
expect(newState.sp.set.home).toBe(0) // Nota: il reset attuale resetta solo i punti o tutto?
|
state.modalitaPartita = "2/3"
|
||||||
// Controllo il codice: "s.sp.punt.home = 0... s.sp.storicoServizio = []"
|
state.sp.set.home = 1
|
||||||
// Attenzione: nel codice originale `resetta` NON sembra resettare i set!
|
state.sp.set.guest = 0
|
||||||
// Verifichiamo il comportamento attuale del codice.
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('modalità 3/5: set non decisivo (2-1) → soglia 25', () => {
|
||||||
|
state.modalitaPartita = "3/5"
|
||||||
|
state.sp.set.home = 2
|
||||||
|
state.sp.set.guest = 1
|
||||||
|
state.sp.punt.home = 15
|
||||||
|
state.sp.punt.guest = 10
|
||||||
|
expect(checkVittoria(state)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// RESET
|
||||||
|
// =============================================
|
||||||
|
describe('Reset', () => {
|
||||||
|
it('dovrebbe resettare punti e set a zero', () => {
|
||||||
|
state.sp.punt.home = 10
|
||||||
|
state.sp.punt.guest = 8
|
||||||
|
state.sp.set.home = 1
|
||||||
|
state.sp.set.guest = 1
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.sp.punt.home).toBe(0)
|
||||||
|
expect(s.sp.punt.guest).toBe(0)
|
||||||
|
expect(s.sp.set.home).toBe(0)
|
||||||
|
expect(s.sp.set.guest).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe resettare formazioni a default', () => {
|
||||||
|
state.sp.form.home = ["10", "11", "12", "13", "14", "15"]
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.sp.form.home).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
expect(s.sp.form.guest).toEqual(["1", "2", "3", "4", "5", "6"])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe resettare la striscia', () => {
|
||||||
|
state.sp.striscia = { home: [0, 1, 2, 3], guest: [0, " ", " ", 1] }
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.sp.striscia.home).toEqual([0])
|
||||||
|
expect(s.sp.striscia.guest).toEqual([0])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe resettare lo storico servizio', () => {
|
||||||
|
state.sp.storicoServizio = [{ servHome: true, cambioPalla: false }]
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.sp.storicoServizio).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe impostare visuForm a false', () => {
|
||||||
|
state.visuForm = true
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.visuForm).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mantenere nomi e modalità', () => {
|
||||||
|
state.sp.nomi.home = "Squadra A"
|
||||||
|
state.modalitaPartita = "2/3"
|
||||||
|
const s = applyAction(state, { type: 'resetta' })
|
||||||
|
expect(s.sp.nomi.home).toBe("Squadra A")
|
||||||
|
expect(s.modalitaPartita).toBe("2/3")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// AZIONE SCONOSCIUTA
|
||||||
|
// =============================================
|
||||||
|
describe('Azione sconosciuta', () => {
|
||||||
|
it('dovrebbe restituire lo stato invariato con azione non riconosciuta', () => {
|
||||||
|
const s = applyAction(state, { type: 'azioneInesistente' })
|
||||||
|
expect(s.sp.punt.home).toBe(0)
|
||||||
|
expect(s.sp.punt.guest).toBe(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,22 +1,148 @@
|
|||||||
import { describe, it, expect } from 'vitest'
|
import { describe, it, expect, vi, afterEach } from 'vitest'
|
||||||
import { printServerInfo } from '../../src/server-utils.js'
|
import * as os from 'os'
|
||||||
|
|
||||||
// Mocking console.log per evitare output sporchi durante i test
|
vi.mock('os', async (importOriginal) => {
|
||||||
import { vi } from 'vitest'
|
return {
|
||||||
|
...await importOriginal(),
|
||||||
|
networkInterfaces: vi.fn(() => ({}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
import { getNetworkIPs, printServerInfo } from '../../src/server-utils.js'
|
||||||
|
|
||||||
describe('Server Utils', () => {
|
describe('Server Utils', () => {
|
||||||
it('printServerInfo dovrebbe stampare le porte corrette', () => {
|
|
||||||
const consoleSpy = vi.spyOn(console, 'log')
|
|
||||||
printServerInfo(3000, 3001)
|
|
||||||
|
|
||||||
expect(consoleSpy).toHaveBeenCalled()
|
afterEach(() => {
|
||||||
|
vi.restoreAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
// Unisce tutti i messaggi loggati in un'unica stringa per facilitare la ricerca
|
// =============================================
|
||||||
const allLogs = consoleSpy.mock.calls.map(args => args[0]).join('\n')
|
// getNetworkIPs
|
||||||
|
// =============================================
|
||||||
|
describe('getNetworkIPs', () => {
|
||||||
|
it('dovrebbe restituire indirizzi IPv4 non-loopback', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.100' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(getNetworkIPs()).toEqual(['192.168.1.100'])
|
||||||
|
})
|
||||||
|
|
||||||
expect(allLogs).toContain('3000')
|
it('dovrebbe escludere indirizzi loopback (internal)', () => {
|
||||||
expect(allLogs).toContain('3001')
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
lo: [
|
||||||
|
{ family: 'IPv4', internal: true, address: '127.0.0.1' }
|
||||||
|
],
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.100' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const ips = getNetworkIPs()
|
||||||
|
expect(ips).not.toContain('127.0.0.1')
|
||||||
|
expect(ips).toContain('192.168.1.100')
|
||||||
|
})
|
||||||
|
|
||||||
consoleSpy.mockRestore()
|
it('dovrebbe escludere indirizzi IPv6', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv6', internal: false, address: 'fe80::1' },
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.100' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const ips = getNetworkIPs()
|
||||||
|
expect(ips).toEqual(['192.168.1.100'])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe escludere bridge Docker 172.17.x.x', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
docker0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '172.17.0.1' }
|
||||||
|
],
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '10.0.0.5' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const ips = getNetworkIPs()
|
||||||
|
expect(ips).not.toContain('172.17.0.1')
|
||||||
|
expect(ips).toContain('10.0.0.5')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe escludere bridge Docker 172.18.x.x', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
br0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '172.18.0.1' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
expect(getNetworkIPs()).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe restituire array vuoto se nessuna interfaccia disponibile', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({})
|
||||||
|
expect(getNetworkIPs()).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe restituire più indirizzi da interfacce diverse', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.100' }
|
||||||
|
],
|
||||||
|
wlan0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.101' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const ips = getNetworkIPs()
|
||||||
|
expect(ips).toHaveLength(2)
|
||||||
|
expect(ips).toContain('192.168.1.100')
|
||||||
|
expect(ips).toContain('192.168.1.101')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================
|
||||||
|
// printServerInfo
|
||||||
|
// =============================================
|
||||||
|
describe('printServerInfo', () => {
|
||||||
|
it('dovrebbe stampare le porte corrette (default)', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({})
|
||||||
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||||
|
printServerInfo()
|
||||||
|
const allLogs = consoleSpy.mock.calls.map(c => c[0]).join('\n')
|
||||||
|
expect(allLogs).toContain('5173')
|
||||||
|
expect(allLogs).toContain('3001')
|
||||||
|
consoleSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe stampare le porte personalizzate', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({})
|
||||||
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||||
|
printServerInfo(3000, 4000)
|
||||||
|
const allLogs = consoleSpy.mock.calls.map(c => c[0]).join('\n')
|
||||||
|
expect(allLogs).toContain('3000')
|
||||||
|
expect(allLogs).toContain('4000')
|
||||||
|
consoleSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('dovrebbe mostrare gli URL remoti se ci sono IP di rete', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({
|
||||||
|
eth0: [
|
||||||
|
{ family: 'IPv4', internal: false, address: '192.168.1.50' }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||||
|
printServerInfo(3000, 3001)
|
||||||
|
const allLogs = consoleSpy.mock.calls.map(c => c[0]).join('\n')
|
||||||
|
expect(allLogs).toContain('192.168.1.50')
|
||||||
|
expect(allLogs).toContain('remoti')
|
||||||
|
consoleSpy.mockRestore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('non dovrebbe mostrare sezione remoti se nessun IP di rete', () => {
|
||||||
|
os.networkInterfaces.mockReturnValue({})
|
||||||
|
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {})
|
||||||
|
printServerInfo(3000, 3001)
|
||||||
|
const allLogs = consoleSpy.mock.calls.map(c => c[0]).join('\n')
|
||||||
|
expect(allLogs).not.toContain('remoti')
|
||||||
|
consoleSpy.mockRestore()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,9 +1,19 @@
|
|||||||
import { defineConfig } from 'vitest/config'
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
plugins: [vue()],
|
||||||
test: {
|
test: {
|
||||||
include: ['tests/unit/**/*.{test,spec}.js', 'tests/integration/**/*.{test,spec}.js'],
|
include: [
|
||||||
globals: true, // permette di usare describe/it/expect senza import
|
'tests/unit/**/*.{test,spec}.js',
|
||||||
environment: 'node', // per backend tests. Se testi componenti Vue, usa 'jsdom'
|
'tests/integration/**/*.{test,spec}.js',
|
||||||
|
'tests/component/**/*.{test,spec}.js',
|
||||||
|
'tests/stress/**/*.{test,spec}.js',
|
||||||
|
],
|
||||||
|
globals: true,
|
||||||
|
environment: 'node',
|
||||||
|
environmentMatchGlobs: [
|
||||||
|
['tests/component/**', 'happy-dom'],
|
||||||
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user