diff --git a/.gitattributes b/.gitattributes index f98e611..92cdbf0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -47,6 +47,7 @@ /stryker.config.mjs export-ignore /tests export-ignore /vite.config.js export-ignore +/vitest.config.js export-ignore # Avoid merge conflicts in CHANGELOG # https://about.gitlab.com/2015/02/10/gitlab-reduced-merge-conflicts-by-90-percent-with-changelog-placeholders/ diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0857d..570b38b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,3 +34,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - refactor(ui): replace the installed-extension tag cloud with a compact Composer namespace ledger. - feat(ui): improve Request disclosures, body limits, history navigation, and table spacing. - fix(ui): show short class names with full hover titles, standardize Timeline dots and bars on one blue token, and remove the redundant category legend. +- test: migrate JavaScript unit and mutation tests to Vitest with isolated workers, V8 coverage, per-test mutation analysis, and supported Node.js release ranges. diff --git a/package-lock.json b/package-lock.json index a0784ac..30b0727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,17 @@ "@fontsource/jetbrains-mono": "^5.3.0", "@playwright/test": "^1.62.1", "@stryker-mutator/core": "^10.0.0", - "c8": "^12.0.0", + "@stryker-mutator/vitest-runner": "^10.0.0", + "@vitest/coverage-v8": "^4.1.11", "eslint": "^10.8.0", "prettier": "^3.9.6", "stylelint": "^17.14.1", "stylelint-config-standard-scss": "^17.0.0", - "vite": "^8.2.1" + "vite": "^8.2.1", + "vitest": "^4.1.11" }, "engines": { - "node": ">=22.12.0" + "node": "^22.12.0 || >=24.0.0" } }, "node_modules/@axe-core/playwright": { @@ -1399,16 +1401,6 @@ } } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", - "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -1819,6 +1811,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@stryker-mutator/api": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@stryker-mutator/api/-/api-10.0.0.tgz", @@ -1909,6 +1908,44 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@stryker-mutator/vitest-runner": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@stryker-mutator/vitest-runner/-/vitest-runner-10.0.0.tgz", + "integrity": "sha512-SHK2/vfvRUpiz7jXPnQMBnr6zLdm69DK03Mo5mPhaZWcRSygrKUqYsPqWsXsK+5ySHzlMTfCyFK5NQ/X9sJFFw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@stryker-mutator/api": "10.0.0", + "@stryker-mutator/util": "10.0.0", + "semver": "^7.7.4", + "tslib": "~2.8.0" + }, + "engines": { + "node": ">=22.0.0" + }, + "peerDependencies": { + "@stryker-mutator/core": "10.0.0", + "vitest": ">=2.0.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", @@ -1930,13 +1967,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/jsesc": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", @@ -1951,6 +1981,150 @@ "dev": true, "license": "MIT" }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.11", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.11", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.11", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.11", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -2037,6 +2211,28 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz", + "integrity": "sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -2140,40 +2336,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/c8": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-12.0.0.tgz", - "integrity": "sha512-4zpJvrd1nKWutnnKC2pXkFmb6iM1l+ffN//o1CzlTNwW7GSOs9a1xrLqkC48nU8oEkjmPZLPiwMsIaOvoF4Pqg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.1", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^3.1.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "test-exclude": "^8.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^18.0.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - }, - "peerDependencies": { - "monocart-coverage-reports": "^2" - }, - "peerDependenciesMeta": { - "monocart-coverage-reports": { - "optional": true - } - } - }, "node_modules/cacheable": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", @@ -2260,6 +2422,16 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -2290,39 +2462,6 @@ "node": ">= 12" } }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2578,6 +2717,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -2783,6 +2929,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2820,6 +2976,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3015,23 +3181,6 @@ "dev": true, "license": "ISC" }, - "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, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -3067,16 +3216,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/get-east-asian-width": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", @@ -3146,24 +3285,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4091,6 +4212,78 @@ "node": "20 || >=22" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz", + "integrity": "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/magicast/node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/magicast/node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/magicast/node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/magicast/node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4195,16 +4388,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4509,22 +4692,12 @@ "node": ">=8" } }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", @@ -5033,6 +5206,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -5097,6 +5277,20 @@ "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.2.tgz", @@ -5455,19 +5649,21 @@ "node": ">=8" } }, - "node_modules/test-exclude": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", - "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^13.0.6", - "minimatch": "^10.2.2" - }, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "dev": true, + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": ">=18" } }, "node_modules/tinyglobby": { @@ -5518,6 +5714,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", + "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5666,21 +5872,6 @@ "dev": true, "license": "MIT" }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/vite": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", @@ -5787,6 +5978,109 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/vitest": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/weapon-regex": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/weapon-regex/-/weapon-regex-2.0.4.tgz", @@ -5810,63 +6104,31 @@ "node": ">= 8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" + "siginfo": "^2.0.0", + "stackback": "0.0.2" }, - "engines": { - "node": ">=18" + "bin": { + "why-is-node-running": "cli.js" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/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, - "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8" } }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, "node_modules/write-file-atomic": { @@ -5882,54 +6144,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.1.0.tgz", - "integrity": "sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^8.2.1", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 6f4eaf6..12bff21 100644 --- a/package.json +++ b/package.json @@ -6,13 +6,13 @@ "scripts": { "build": "vite build", "dev": "vite build --watch", - "format": "prettier --write 'resources/{src,tests}/**/*.js' 'e2e/**/*.js' 'tools/**/*.{mjs,json}' 'docs/**/*.md' 'playwright.config.js' 'vite.config.js' 'stryker.config.mjs' 'package.json'", - "format:check": "prettier --check 'resources/{src,tests}/**/*.js' 'e2e/**/*.js' 'tools/**/*.{mjs,json}' 'docs/**/*.md' 'playwright.config.js' 'vite.config.js' 'stryker.config.mjs' 'package.json'", + "format": "prettier --write 'resources/{src,tests}/**/*.js' 'e2e/**/*.js' 'tools/**/*.{mjs,json}' 'docs/**/*.md' 'playwright.config.js' 'vite.config.js' 'vitest.config.js' 'stryker.config.mjs' 'package.json'", + "format:check": "prettier --check 'resources/{src,tests}/**/*.js' 'e2e/**/*.js' 'tools/**/*.{mjs,json}' 'docs/**/*.md' 'playwright.config.js' 'vite.config.js' 'vitest.config.js' 'stryker.config.mjs' 'package.json'", "lint:css": "stylelint --config .github/linters/.stylelintrc.json --config-basedir . 'resources/src/**/*.css'", "lint:css:fix": "stylelint --config .github/linters/.stylelintrc.json --config-basedir . --fix 'resources/src/**/*.css'", - "lint:js": "eslint --config .github/linters/eslint.config.mjs resources e2e tools playwright.config.js", - "lint:js:fix": "eslint --config .github/linters/eslint.config.mjs --fix resources e2e tools playwright.config.js", - "test:js": "c8 node --test resources/tests/*.test.js", + "lint:js": "eslint --config .github/linters/eslint.config.mjs resources e2e tools playwright.config.js vitest.config.js", + "lint:js:fix": "eslint --config .github/linters/eslint.config.mjs --fix resources e2e tools playwright.config.js vitest.config.js", + "test:js": "vitest run --coverage", "test:mutation": "stryker run", "fixtures:seed": "php tools/seed-debug-fixtures.php", "check:size": "node tools/check-asset-size.mjs", @@ -29,25 +29,8 @@ "test:perf:live": "playwright test e2e/live-performance.spec.js --project=desktop-1440", "test:browser": "playwright test e2e/smoke.spec.js e2e/security.spec.js e2e/accessibility.spec.js e2e/visual.spec.js" }, - "c8": { - "all": true, - "include": [ - "resources/src/**/*.js" - ], - "exclude": [ - "resources/src/core/debug.js", - "resources/src/toolbar/element.js", - "resources/src/toolbar/index.js" - ], - "reports-dir": "runtime/coverage-js", - "temp-directory": "runtime/coverage-js/tmp", - "check-coverage": true, - "lines": 100, - "branches": 100, - "functions": 100 - }, "engines": { - "node": ">=22.12.0" + "node": "^22.12.0 || >=24.0.0" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", @@ -55,12 +38,14 @@ "@fontsource/jetbrains-mono": "^5.3.0", "@playwright/test": "^1.62.1", "@stryker-mutator/core": "^10.0.0", - "c8": "^12.0.0", + "@stryker-mutator/vitest-runner": "^10.0.0", + "@vitest/coverage-v8": "^4.1.11", "eslint": "^10.8.0", "prettier": "^3.9.6", "stylelint": "^17.14.1", "stylelint-config-standard-scss": "^17.0.0", - "vite": "^8.2.1" + "vite": "^8.2.1", + "vitest": "^4.1.11" }, "license": "BSD-3-Clause" } diff --git a/resources/tests/clipboard.test.js b/resources/tests/clipboard.test.js index 55e64bf..71707a3 100644 --- a/resources/tests/clipboard.test.js +++ b/resources/tests/clipboard.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { bindCopyControls, copyControlText } from "../src/core/clipboard.js"; diff --git a/resources/tests/db.test.js b/resources/tests/db.test.js index 3c2f353..a6b7d3f 100644 --- a/resources/tests/db.test.js +++ b/resources/tests/db.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; class ClassList { constructor() { @@ -248,6 +248,28 @@ test("N+1 group links filter query rows and expose live progress", () => { assert.equal(clear.hidden, true); }); +test("N+1 filtering tolerates orphan markers and missing optional controls", () => { + var orphan = new Element(); + + orphan.setAttribute("data-yii-debug-n1-group", "group-orphan"); + orphan.closest = () => null; + + var root = { + querySelector() { + return null; + }, + querySelectorAll(selector) { + return selector === "[data-yii-debug-n1-group]" ? [orphan] : []; + }, + }; + + assert.deepEqual(applyNPlusOneFilter(root, "group-orphan"), { + activeGroup: "group-orphan", + total: 0, + visible: 0, + }); +}); + test("native explain-all control synchronizes batch state and loading semantics", () => { assert.equal(explainAll.textContent, "Explain all"); assert.equal(explainAll.getAttribute("aria-expanded"), "false"); @@ -327,15 +349,37 @@ test("collapsing an EXPLAIN batch cancels active and queued requests", () => { }); var requestCount = requests.length; + var originalShift = Array.prototype.shift; - explainAll.dispatchEvent(new Event("click")); + Array.prototype.shift = function () { + var task = originalShift.call(this); + + if (task && task.toggle === toggles[0]) { + task.cancelled = true; + } + + return task; + }; + + try { + explainAll.dispatchEvent(new Event("click")); + } finally { + Array.prototype.shift = originalShift; + } assert.equal(requests.length, requestCount + 3); + assert.equal(requests[requestCount].url, toggles[1].href); assert.equal(explainAll.textContent, "Explaining 0/5"); var activeRequests = requests.slice(requestCount); - explainAll.dispatchEvent(new Event("click")); + toggles[4].closest = () => null; + + try { + explainAll.dispatchEvent(new Event("click")); + } finally { + toggles[4].closest = () => containers[4]; + } assert.equal(requests.length, requestCount + 3); activeRequests.forEach((request) => { diff --git a/resources/tests/deep-links.test.js b/resources/tests/deep-links.test.js index 1b7ac02..b524182 100644 --- a/resources/tests/deep-links.test.js +++ b/resources/tests/deep-links.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { enhanceSectionPermalinks, diff --git a/resources/tests/dom.test.js b/resources/tests/dom.test.js index 1b5683d..368cbdb 100644 --- a/resources/tests/dom.test.js +++ b/resources/tests/dom.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { AJAX_TIMEOUT, ajax, on } from "../src/core/dom.js"; @@ -197,7 +197,11 @@ test("ajax settles once and ignores non-final ready states", () => { request.status = 500; ajax("https://example.test/debug/db-explain"); + request.readyState = 4; + request.status = 500; request.onreadystatechange(); + + ajax("https://example.test/debug/db-explain"); request.onabort(); request.ontimeout(); diff --git a/resources/tests/dropdown.test.js b/resources/tests/dropdown.test.js index 8feb7d0..d30650f 100644 --- a/resources/tests/dropdown.test.js +++ b/resources/tests/dropdown.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { dropdownNavigationIndex } from "../src/core/dropdown.js"; diff --git a/resources/tests/features.test.js b/resources/tests/features.test.js index c2a9479..ff59aef 100644 --- a/resources/tests/features.test.js +++ b/resources/tests/features.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { loadPanelFeatures, diff --git a/resources/tests/history-cursor-default.test.js b/resources/tests/history-cursor-default.test.js index 9ca4779..df4b865 100644 --- a/resources/tests/history-cursor-default.test.js +++ b/resources/tests/history-cursor-default.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var scrolls = []; var clickHandler = null; diff --git a/resources/tests/history-cursor-no-rows.test.js b/resources/tests/history-cursor-no-rows.test.js index 54a345d..45b68a0 100644 --- a/resources/tests/history-cursor-no-rows.test.js +++ b/resources/tests/history-cursor-no-rows.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var sectionCalls = 0; diff --git a/resources/tests/history-cursor-no-section.test.js b/resources/tests/history-cursor-no-section.test.js index eecb211..633fb5d 100644 --- a/resources/tests/history-cursor-no-section.test.js +++ b/resources/tests/history-cursor-no-section.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var rowQueries = 0; diff --git a/resources/tests/history-cursor.test.js b/resources/tests/history-cursor.test.js index 41a90a3..4bd9a7f 100644 --- a/resources/tests/history-cursor.test.js +++ b/resources/tests/history-cursor.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var prevented = 0; var scrolls = []; diff --git a/resources/tests/phpinfo-search-init.test.js b/resources/tests/phpinfo-search-init.test.js index 1929491..96e3d0f 100644 --- a/resources/tests/phpinfo-search-init.test.js +++ b/resources/tests/phpinfo-search-init.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; class ClassList { constructor() { diff --git a/resources/tests/phpinfo-search.test.js b/resources/tests/phpinfo-search.test.js index 8c81058..991a79e 100644 --- a/resources/tests/phpinfo-search.test.js +++ b/resources/tests/phpinfo-search.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { formatPhpInfoFilteredCount, diff --git a/resources/tests/tabs.test.js b/resources/tests/tabs.test.js index cf971fb..257fe19 100644 --- a/resources/tests/tabs.test.js +++ b/resources/tests/tabs.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { activateTab, diff --git a/resources/tests/theme.test.js b/resources/tests/theme.test.js index 134c7fb..fc1baba 100644 --- a/resources/tests/theme.test.js +++ b/resources/tests/theme.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { addThemeToDebugUrl, diff --git a/resources/tests/toolbar-messaging-nofetch.test.js b/resources/tests/toolbar-messaging-nofetch.test.js index 1b8f14a..f38a8de 100644 --- a/resources/tests/toolbar-messaging-nofetch.test.js +++ b/resources/tests/toolbar-messaging-nofetch.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; globalThis.window = { location: new URL("https://example.test/"), diff --git a/resources/tests/toolbar-messaging.test.js b/resources/tests/toolbar-messaging.test.js index 3db17aa..890825e 100644 --- a/resources/tests/toolbar-messaging.test.js +++ b/resources/tests/toolbar-messaging.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var failNextFetch = false; var fetchCalls = []; @@ -238,6 +238,13 @@ test("request tracking skips toolbar, cross-origin, and skip-listed URLs", async return name === "data-skip-urls" ? '["/api/health"]' : null; }; + var skippedXhr = new XMLHttpRequest(); + + skippedXhr.open("GET", "/debug/default/toolbar?tag=page"); + assert.equal(skippedXhr.readyState, 1); + assert.equal(skippedXhr.listeners.has("readystatechange"), false); + assert.equal(requestStack.length, startIndex); + await window.fetch(undefined).catch(function () {}); await window.fetch("https://[bad"); await window.fetch("https://attacker.test/api/items"); diff --git a/resources/tests/toolbar-runtime.test.js b/resources/tests/toolbar-runtime.test.js index 1f0b426..16ac9b9 100644 --- a/resources/tests/toolbar-runtime.test.js +++ b/resources/tests/toolbar-runtime.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; import { renderPhpBrand, renderYiiBrand } from "../src/toolbar/brand.js"; import { builtinIconUrl } from "../src/toolbar/icons.js"; diff --git a/resources/tests/toolbar-state.test.js b/resources/tests/toolbar-state.test.js index fce1123..0963a89 100644 --- a/resources/tests/toolbar-state.test.js +++ b/resources/tests/toolbar-state.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; function createStorage() { var items = new Map(); diff --git a/resources/tests/toolbar-theme.test.js b/resources/tests/toolbar-theme.test.js index bf4db02..3648e7c 100644 --- a/resources/tests/toolbar-theme.test.js +++ b/resources/tests/toolbar-theme.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; globalThis.window = { location: new URL("https://example.test/"), diff --git a/resources/tests/userswitch-guard.test.js b/resources/tests/userswitch-guard.test.js index 6964055..5eec212 100644 --- a/resources/tests/userswitch-guard.test.js +++ b/resources/tests/userswitch-guard.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var clickHandler = null; var reloads = 0; diff --git a/resources/tests/userswitch-row.test.js b/resources/tests/userswitch-row.test.js index 94971d2..dbdbdb6 100644 --- a/resources/tests/userswitch-row.test.js +++ b/resources/tests/userswitch-row.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var handlers = {}; var sends = 0; diff --git a/resources/tests/userswitch.test.js b/resources/tests/userswitch.test.js index 047e7a8..732c7b3 100644 --- a/resources/tests/userswitch.test.js +++ b/resources/tests/userswitch.test.js @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import test from "node:test"; +import { test } from "vitest"; var clickHandler = null; var reloads = 0; diff --git a/stryker.config.mjs b/stryker.config.mjs index 24d75bb..a069d2d 100644 --- a/stryker.config.mjs +++ b/stryker.config.mjs @@ -17,11 +17,10 @@ const config = { "resources/src/toolbar/position.js", "resources/src/toolbar/url.js", ], - testRunner: "command", - commandRunner: { - command: "node --test resources/tests/*.test.js", + testRunner: "vitest", + vitest: { + configFile: "vitest.config.js", }, - coverageAnalysis: "off", concurrency: 4, reporters: ["clear-text", "progress", "html"], thresholds: { diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 0000000..8837a37 --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,26 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + coverage: { + provider: "v8", + include: ["resources/src/**/*.js"], + exclude: [ + "resources/src/core/debug.js", + "resources/src/toolbar/element.js", + "resources/src/toolbar/index.js", + ], + reporter: ["text"], + reportsDirectory: "runtime/coverage-js", + thresholds: { + branches: 100, + functions: 100, + lines: 100, + }, + }, + environment: "node", + include: ["resources/tests/**/*.test.js"], + isolate: true, + pool: "threads", + }, +});