diff --git a/.github/workflows/verifications.yml b/.github/workflows/verifications.yml index 92d1b2a4..ccb2b425 100644 --- a/.github/workflows/verifications.yml +++ b/.github/workflows/verifications.yml @@ -31,11 +31,26 @@ jobs: tests: name: Tests (Node v${{ matrix.node }} - ESLint v${{ matrix.eslint }}) runs-on: ubuntu-latest + timeout-minutes: 3 strategy: fail-fast: false matrix: node: [12.22.0, 12, 14.17.0, 14, 16, 17, 18, 19, 20, 22] - eslint: [7.5, 7, 8] + eslint: [7.5, 7, 8, 9] + exclude: + # eslint@9 doesn't support < Node v18 + - node: 17 + eslint: 9 + - node: 16 + eslint: 9 + - node: 14 + eslint: 9 + - node: 14.17.0 + eslint: 9 + - node: 12 + eslint: 9 + - node: 12.22.0 + eslint: 9 steps: - name: Checkout uses: actions/checkout@v4 @@ -49,6 +64,10 @@ jobs: - name: Install dependencies run: npm install + # see https://github.com/npm/cli/issues/7349 + - if: ${{ matrix.eslint == 9 }} + run: npm un @typescript-eslint/eslint-plugin eslint-plugin-jest eslint-doc-generator + - name: Install ESLint v${{ matrix.eslint }} run: npm install --no-save --force eslint@${{ matrix.eslint }} diff --git a/package-lock.json b/package-lock.json index a01dd55f..d6176697 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "npm-run-all2": "^5.0.2", "prettier": "^3.3.3", "semantic-release": "^19.0.5", + "semver": "^7.6.3", "ts-node": "^10.9.2", "typescript": "5.0.4" }, @@ -118,6 +119,16 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/eslint-parser": { "version": "7.25.8", "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.25.8.tgz", @@ -136,6 +147,16 @@ "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0" } }, + "node_modules/@babel/eslint-parser/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/eslint-plugin": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/eslint-plugin/-/eslint-plugin-7.25.7.tgz", @@ -183,6 +204,16 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/@babel/helper-module-imports": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", @@ -3109,18 +3140,6 @@ "node": ">=6" } }, - "node_modules/@semantic-release/npm/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@semantic-release/release-notes-generator": { "version": "10.0.3", "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-10.0.3.tgz", @@ -3650,18 +3669,6 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/parser": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", @@ -3770,17 +3777,6 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", @@ -3806,17 +3802,6 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", @@ -4861,6 +4846,16 @@ "node": ">=10" } }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/conventional-commits-filter": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", @@ -5977,6 +5972,16 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-jest": { "version": "27.9.0", "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", @@ -6034,6 +6039,16 @@ "eslint": ">=5.16.0" } }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/eslint-plugin-promise": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", @@ -7731,18 +7746,6 @@ "semver": "^7.6.3" } }, - "node_modules/is-bun-module/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -8099,6 +8102,16 @@ "node": ">=8" } }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", @@ -10397,18 +10410,6 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-snapshot/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11874,18 +11875,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -12218,18 +12207,6 @@ "node": ">=10" } }, - "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -16131,18 +16108,6 @@ "node": ">=10" } }, - "node_modules/semantic-release/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/semantic-release/node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -16188,12 +16153,15 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-diff": { @@ -16208,6 +16176,16 @@ "node": ">=8" } }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/semver-regex": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz", @@ -17699,6 +17677,14 @@ "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "@babel/eslint-parser": { @@ -17710,6 +17696,14 @@ "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", "eslint-visitor-keys": "^2.1.0", "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "@babel/eslint-plugin": { @@ -17744,6 +17738,14 @@ "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "@babel/helper-module-imports": { @@ -19962,12 +19964,6 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true - }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true } } }, @@ -20355,14 +20351,6 @@ "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - } } }, "@typescript-eslint/parser": { @@ -20415,13 +20403,6 @@ "is-glob": "^4.0.3", "semver": "^7.3.7", "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" - } } }, "@typescript-eslint/utils": { @@ -20437,13 +20418,6 @@ "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" - } } }, "@typescript-eslint/visitor-keys": { @@ -21189,6 +21163,14 @@ "semver": "^6.0.0", "split": "^1.0.0", "through2": "^4.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "conventional-commits-filter": { @@ -22086,6 +22068,12 @@ "requires": { "esutils": "^2.0.2" } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true } } }, @@ -22117,6 +22105,14 @@ "minimatch": "^3.0.4", "resolve": "^1.10.1", "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "eslint-plugin-promise": { @@ -23218,14 +23214,6 @@ "dev": true, "requires": { "semver": "^7.6.3" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - } } }, "is-callable": { @@ -23456,6 +23444,14 @@ "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "istanbul-lib-report": { @@ -25283,12 +25279,6 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -26277,14 +26267,6 @@ "dev": true, "requires": { "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - } } }, "make-error": { @@ -26528,14 +26510,6 @@ "is-core-module": "^2.5.0", "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - } } }, "normalize-path": { @@ -29274,12 +29248,6 @@ "yaml": "^1.10.0" } }, - "semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true - }, "wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -29315,10 +29283,9 @@ } }, "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" }, "semver-diff": { "version": "3.1.1", @@ -29327,6 +29294,14 @@ "dev": true, "requires": { "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } } }, "semver-regex": { diff --git a/package.json b/package.json index 7f5159fc..8bfa55a2 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "npm-run-all2": "^5.0.2", "prettier": "^3.3.3", "semantic-release": "^19.0.5", + "semver": "^7.6.3", "ts-node": "^10.9.2", "typescript": "5.0.4" }, diff --git a/tests/lib/FlatCompatRuleTester.ts b/tests/lib/FlatCompatRuleTester.ts new file mode 100644 index 00000000..b3ef5943 --- /dev/null +++ b/tests/lib/FlatCompatRuleTester.ts @@ -0,0 +1,191 @@ +import { TSESLint } from '@typescript-eslint/utils'; +import { version as eslintVersion } from 'eslint/package.json'; +import * as semver from 'semver'; + +export const usingFlatConfig = semver.major(eslintVersion) >= 9; + +declare module '@typescript-eslint/utils/dist/ts-eslint' { + // eslint-disable-next-line @typescript-eslint/no-namespace + export namespace FlatConfig { + export interface LinterOptions { + /** + * A Boolean value indicating if inline configuration is allowed. + */ + noInlineConfig?: boolean; + /** + * A severity string indicating if and how unused disable and enable + * directives should be tracked and reported. For legacy compatibility, `true` + * is equivalent to `"warn"` and `false` is equivalent to `"off"`. + * @default "off" + */ + reportUnusedDisableDirectives?: + | TSESLint.Linter.Severity + | TSESLint.Linter.SeverityString + | boolean; + } + + export interface Config { + /** + * An string to identify the configuration object. Used in error messages and inspection tools. + */ + name?: string; + /** + * An array of glob patterns indicating the files that the configuration object should apply to. + * If not specified, the configuration object applies to all files matched by any other configuration object. + */ + files?: (string | string[])[]; + /** + * An array of glob patterns indicating the files that the configuration object should not apply to. + * If not specified, the configuration object applies to all files matched by files. + */ + ignores?: string[]; + /** + * An object containing settings related to how JavaScript is configured for linting. + */ + languageOptions?: LanguageOptions; + /** + * An object containing settings related to the linting process. + */ + linterOptions?: LinterOptions; + /** + * An object containing a name-value mapping of plugin names to plugin objects. + * When `files` is specified, these plugins are only available to the matching files. + */ + plugins?: unknown; + /** + * Either an object containing `preprocess()` and `postprocess()` methods or + * a string indicating the name of a processor inside of a plugin + * (i.e., `"pluginName/processorName"`). + */ + processor?: string | TSESLint.Linter.Processor; + /** + * An object containing the configured rules. + * When `files` or `ignores` are specified, these rule configurations are only available to the matching files. + */ + rules?: TSESLint.Linter.RulesRecord; + /** + * An object containing name-value pairs of information that should be available to all rules. + */ + settings?: TSESLint.SharedConfigurationSettings; + } + + export type ParserOptions = TSESLint.Linter.ParserOptions; + + export interface LanguageOptions { + /** + * The version of ECMAScript to support. + * May be any year (i.e., `2022`) or version (i.e., `5`). + * Set to `"latest"` for the most recent supported version. + * @default "latest" + */ + ecmaVersion?: Required['ecmaVersion']; + /** + * An object specifying additional objects that should be added to the global scope during linting. + */ + globals?: + | Record + | undefined; + /** + * An object containing a `parse()` method or a `parseForESLint()` method. + * @default + * ``` + * // https://github.com/eslint/espree + * require('espree') + * ``` + */ + parser?: unknown; + /** + * An object specifying additional options that are passed directly to the parser. + * The available options are parser-dependent. + */ + parserOptions?: ParserOptions | undefined; + /** + * The type of JavaScript source code. + * Possible values are `"script"` for traditional script files, `"module"` for ECMAScript modules (ESM), and `"commonjs"` for CommonJS files. + * @default + * ``` + * // for `.js` and `.mjs` files + * "module" + * // for `.cjs` files + * "commonjs" + * ``` + */ + sourceType?: Required['sourceType']; + } + } +} + +export class FlatCompatRuleTester extends TSESLint.RuleTester { + public constructor(testerConfig?: TSESLint.RuleTesterConfig) { + super(FlatCompatRuleTester._flatCompat(testerConfig)); + } + + public override run< + TMessageIds extends string, + TOptions extends readonly unknown[], + >( + ruleName: string, + rule: TSESLint.RuleModule, + tests: TSESLint.RunTests + ) { + super.run(ruleName, rule, { + valid: tests.valid.map((t) => FlatCompatRuleTester._flatCompat(t)), + invalid: tests.invalid.map((t) => FlatCompatRuleTester._flatCompat(t)), + }); + } + + /* istanbul ignore next */ + private static _flatCompat< + T extends + | undefined + | TSESLint.RuleTesterConfig + | string + | TSESLint.ValidTestCase + | TSESLint.InvalidTestCase, + >(config: T): T { + if (!config || !usingFlatConfig || typeof config === 'string') { + return config; + } + + const obj: TSESLint.FlatConfig.Config & { + languageOptions: TSESLint.FlatConfig.LanguageOptions & { + parserOptions: TSESLint.FlatConfig.ParserOptions; + }; + } = { + languageOptions: { parserOptions: {} }, + }; + + for (const [key, value] of Object.entries(config)) { + if (key === 'parser') { + obj.languageOptions.parser = require(value as string); + + continue; + } + + if (key === 'parserOptions') { + for (const [option, val] of Object.entries( + value as { [s: string]: unknown } + )) { + if (option === 'ecmaVersion' || option === 'sourceType') { + // @ts-expect-error: TS thinks the value could the opposite type of whatever option is + obj.languageOptions[option] = + val as TSESLint.FlatConfig.LanguageOptions[ + | 'ecmaVersion' + | 'sourceType']; + + continue; + } + + obj.languageOptions.parserOptions[option] = val; + } + + continue; + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + obj[key as keyof typeof obj] = value; + } + + return obj as unknown as T; + } +} diff --git a/tests/lib/rules/await-async-events.test.ts b/tests/lib/rules/await-async-events.test.ts index 82a9c347..80ffb15d 100644 --- a/tests/lib/rules/await-async-events.test.ts +++ b/tests/lib/rules/await-async-events.test.ts @@ -131,7 +131,7 @@ ruleTester.run(RULE_NAME, rule, { doSomething() return fireEvent.${eventMethod}(getByLabelText('username')) } - + await triggerEvent() }) `, @@ -142,7 +142,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - import { fireEvent } from 'somewhere-else' + import { fireEvent } from 'somewhere-else' // not using ${testingFramework} test('unhandled promise from event not related to TL is valid', async () => { fireEvent.${eventMethod}(getByLabelText('username')) }) @@ -154,7 +154,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - import { fireEvent } from 'test-utils' + import { fireEvent } from 'test-utils' // implicitly using ${testingFramework} test('await promise from event method imported from custom module is valid', async () => { await fireEvent.${eventMethod}(getByLabelText('username')) }) @@ -166,13 +166,13 @@ ruleTester.run(RULE_NAME, rule, { // valid use case without call expression // so there is no innermost function scope found code: ` - import { fireEvent } from 'test-utils' + import { fireEvent } from 'test-utils' // implicitly using ${testingFramework} test('edge case for innermost function without call expression', async () => { function triggerEvent() { doSomething() return fireEvent.focus(getByLabelText('username')) } - + const reassignedFunction = triggerEvent }) `, @@ -318,7 +318,7 @@ ruleTester.run(RULE_NAME, rule, { doSomething() return userEvent.${eventMethod}(getByLabelText('username')) } - + await triggerEvent() }) `, @@ -380,7 +380,7 @@ ruleTester.run(RULE_NAME, rule, { doSomething() return userEvent.focus(getByLabelText('username')) } - + const reassignedFunction = triggerEvent }) `, @@ -447,11 +447,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], options: [{ eventModule: 'fireEvent' }], - output: ` - import { fireEvent } from '${testingFramework}' - - fireEvent.${eventMethod}(getByLabelText('username')) - `, + output: null, }) as const ), ...FIRE_EVENT_ASYNC_FUNCTIONS.map( @@ -611,7 +607,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - import { fireEvent } from 'test-utils' + import { fireEvent } from 'test-utils' // implicitly using ${testingFramework} test( 'unhandled promise from event method imported from custom module with aggressive reporting opted-out is invalid', () => { @@ -628,7 +624,7 @@ ruleTester.run(RULE_NAME, rule, { ], options: [{ eventModule: 'fireEvent' }], output: ` - import { fireEvent } from 'test-utils' + import { fireEvent } from 'test-utils' // implicitly using ${testingFramework} test( 'unhandled promise from event method imported from custom module with aggressive reporting opted-out is invalid', async () => { @@ -759,16 +755,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], options: [{ eventModule: 'fireEvent' }], - output: ` - import { fireEvent } from '${testingFramework}' - - function triggerEvent() { - doSomething() - return fireEvent.${eventMethod}(getByLabelText('username')) - } - - triggerEvent() - `, + output: null, }) as const ), ]), @@ -805,7 +792,7 @@ ruleTester.run(RULE_NAME, rule, { ({ code: ` import userEvent from '${testingFramework}' - + userEvent.${eventMethod}(getByLabelText('username')) `, errors: [ @@ -818,11 +805,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], options: [{ eventModule: 'userEvent' }], - output: ` - import userEvent from '${testingFramework}' - - userEvent.${eventMethod}(getByLabelText('username')) - `, + output: null, }) as const ), ...USER_EVENT_ASYNC_FUNCTIONS.map( @@ -974,16 +957,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], options: [{ eventModule: 'userEvent' }], - output: ` - import userEvent from '${testingFramework}' - - function triggerEvent() { - doSomething() - return userEvent.${eventMethod}(getByLabelText('username')) - } - - triggerEvent() - `, + output: null, }) as const ), ...USER_EVENT_ASYNC_FUNCTIONS.map( diff --git a/tests/lib/rules/await-async-utils.test.ts b/tests/lib/rules/await-async-utils.test.ts index 1a5490c9..fc538079 100644 --- a/tests/lib/rules/await-async-utils.test.ts +++ b/tests/lib/rules/await-async-utils.test.ts @@ -113,9 +113,9 @@ ruleTester.run(RULE_NAME, rule, { const aPromise = ${asyncUtil}(() => document.querySelector('div.getOuttaHere') ); - + doSomethingElse(); - + return aPromise; }; }); @@ -126,7 +126,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - import { ${asyncUtil} } from 'some-other-library'; + import { ${asyncUtil} } from 'some-other-library'; // rather than ${testingFramework} test( 'aggressive reporting disabled - util "${asyncUtil}" which is not related to testing library is valid', async () => { @@ -140,7 +140,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - import * as asyncUtils from 'some-other-library'; + import * as asyncUtils from 'some-other-library'; // rather than ${testingFramework} test( 'aggressive reporting disabled - util "asyncUtils.${asyncUtil}" which is not related to testing library is valid', async () => { @@ -228,7 +228,7 @@ ruleTester.run(RULE_NAME, rule, { ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` import { ${asyncUtil} } from '${testingFramework}'; - + function waitForSomethingAsync() { return ${asyncUtil}(() => somethingAsync()) } @@ -242,7 +242,7 @@ ruleTester.run(RULE_NAME, rule, { code: ` test('using unrelated promises with Promise.all is valid', async () => { Promise.all([ - waitForNotRelatedToTestingLibrary(), + waitForNotRelatedToTestingLibrary(), // completely unrelated to ${testingFramework} promise1, await foo().then(() => baz()) ]) @@ -262,13 +262,14 @@ ruleTester.run(RULE_NAME, rule, { }, ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -286,7 +287,7 @@ ruleTester.run(RULE_NAME, rule, { const { waitForAsyncUtil: myDestructuredAlias } = setup(); await myDestructuredAlias(); - + const { user, ...rest } = setup(); await rest.waitForAsyncUtil(); @@ -297,24 +298,24 @@ ruleTester.run(RULE_NAME, rule, { ...ASYNC_UTILS.map((asyncUtil) => ({ code: ` import React from 'react'; - import { render, act } from '@testing-library/react'; - + import { render, act } from '${testingFramework}'; + const doWithAct = async (timeout) => { await act(async () => await ${asyncUtil}(screen.getByTestId('my-test'))); }; - + describe('Component', () => { const mock = jest.fn(); - + it('test', async () => { let Component = () => { mock(1); return
; }; render(); - + await doWithAct(500); - + const myNumberTestVar = 1; const myBooleanTestVar = false; const myArrayTestVar = [1, 2]; @@ -467,7 +468,7 @@ ruleTester.run(RULE_NAME, rule, { ({ code: ` import { ${asyncUtil}, render } from '${testingFramework}'; - + function waitForSomethingAsync() { return ${asyncUtil}(() => somethingAsync()) } @@ -491,7 +492,7 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` - import { ${asyncUtil} } from 'some-other-library'; + import { ${asyncUtil} } from 'some-other-library'; // rather than ${testingFramework} test( 'aggressive reporting - util "${asyncUtil}" which is not related to testing library is invalid', async () => { @@ -514,7 +515,7 @@ ruleTester.run(RULE_NAME, rule, { ({ code: ` import { ${asyncUtil}, render } from '${testingFramework}'; - + function waitForSomethingAsync() { return ${asyncUtil}(() => somethingAsync()) } @@ -539,7 +540,7 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` - import * as asyncUtils from 'some-other-library'; + import * as asyncUtils from 'some-other-library'; // rather than ${testingFramework} test( 'aggressive reporting - util "asyncUtils.${asyncUtil}" which is not related to testing library is invalid', async () => { @@ -561,13 +562,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -578,7 +580,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 14, + line: 15, column: 11, messageId: 'asyncUtilWrapper', data: { name: 'waitForAsyncUtil' }, @@ -590,13 +592,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -608,7 +611,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 15, + line: 16, column: 11, messageId: 'asyncUtilWrapper', data: { name: 'myAlias' }, @@ -620,13 +623,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -637,7 +641,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 14, + line: 15, column: 17, messageId: 'asyncUtilWrapper', data: { name: 'waitForAsyncUtil' }, @@ -649,13 +653,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -666,7 +671,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 14, + line: 15, column: 11, messageId: 'asyncUtilWrapper', data: { name: 'myAlias' }, @@ -678,13 +683,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -694,7 +700,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 13, + line: 14, column: 19, messageId: 'asyncUtilWrapper', data: { name: 'waitForAsyncUtil' }, @@ -706,13 +712,14 @@ ruleTester.run(RULE_NAME, rule, { (asyncUtil) => ({ code: ` + // implicitly using ${testingFramework} function setup() { const utils = render(); - + const waitForAsyncUtil = () => { return ${asyncUtil}(screen.queryByTestId('my-test-id')); }; - + return { waitForAsyncUtil, ...utils }; } @@ -723,7 +730,7 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [ { - line: 14, + line: 15, column: 11, messageId: 'asyncUtilWrapper', data: { name: 'myAlias' }, diff --git a/tests/lib/rules/no-dom-import.test.ts b/tests/lib/rules/no-dom-import.test.ts index e515eb5c..e5fbce0c 100644 --- a/tests/lib/rules/no-dom-import.test.ts +++ b/tests/lib/rules/no-dom-import.test.ts @@ -31,22 +31,38 @@ ruleTester.run(RULE_NAME, rule, { 'import { foo } from "foo"', 'import "foo"', ...SUPPORTED_TESTING_FRAMEWORKS.flatMap(({ oldName, newName }) => - [oldName, newName].flatMap((testingFramework) => [ - `import { fireEvent } from "${testingFramework}"`, - `import * as testing from "${testingFramework}"`, - `import "${testingFramework}"`, - ]) + [oldName, newName !== oldName ? newName : null].flatMap( + (testingFramework) => { + if (!testingFramework) { + return []; + } + + return [ + `import { fireEvent } from "${testingFramework}"`, + `import * as testing from "${testingFramework}"`, + `import "${testingFramework}"`, + ]; + } + ) ), 'const { foo } = require("foo")', 'require("foo")', 'require("")', 'require()', ...SUPPORTED_TESTING_FRAMEWORKS.flatMap(({ oldName, newName }) => - [oldName, newName].flatMap((testingFramework) => [ - `const { fireEvent } = require("${testingFramework}")`, - `const { fireEvent: testing } = require("${testingFramework}")`, - `require("${testingFramework}")`, - ]) + [oldName, newName !== oldName ? newName : null].flatMap( + (testingFramework) => { + if (!testingFramework) { + return []; + } + + return [ + `const { fireEvent } = require("${testingFramework}")`, + `const { fireEvent: testing } = require("${testingFramework}")`, + `require("${testingFramework}")`, + ]; + } + ) ), { code: 'import { fireEvent } from "test-utils"', @@ -61,7 +77,7 @@ ruleTester.run(RULE_NAME, rule, { messageId: 'noDomImport', }, ], - output: 'import { fireEvent } from "dom-testing-library"', + output: null, }, { settings: { @@ -76,9 +92,7 @@ ruleTester.run(RULE_NAME, rule, { messageId: 'noDomImport', }, ], - output: ` - // case: dom-testing-library imported with custom module setting - import { fireEvent } from "dom-testing-library"`, + output: null, }, ...SUPPORTED_TESTING_FRAMEWORKS.flatMap( ({ configOption, oldName, newName }) => diff --git a/tests/lib/rules/no-node-access.test.ts b/tests/lib/rules/no-node-access.test.ts index f3216e7e..cdecd5a9 100644 --- a/tests/lib/rules/no-node-access.test.ts +++ b/tests/lib/rules/no-node-access.test.ts @@ -61,7 +61,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - // case: code not related to testing library at all + // case: code not related to ${testingFramework} at all ReactDOM.render( @@ -116,14 +116,14 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - // case: custom module set but not imported (aggressive reporting limited) + // case: custom module set but not imported using ${testingFramework} (aggressive reporting limited) const closestButton = document.getElementById('submit-btn').closest('button'); expect(closestButton).toBeInTheDocument(); `, }, { code: ` - // case: without importing TL (aggressive reporting skipped) + // case: without importing ${testingFramework} (aggressive reporting skipped) const closestButton = document.getElementById('submit-btn') expect(closestButton).toBeInTheDocument(); `, @@ -141,7 +141,7 @@ ruleTester.run(RULE_NAME, rule, { { // Example from discussions in issue #386 code: ` - import { render } from '@testing-library/react'; + import { render } from '${testingFramework}'; function Wrapper({ children }) { // this should NOT be reported @@ -165,7 +165,7 @@ ruleTester.run(RULE_NAME, rule, { 'testing-library/utils-module': 'test-utils', }, code: ` - // case: importing from custom module (aggressive reporting limited) + // case: importing from custom module for ${testingFramework} (aggressive reporting limited) import 'test-utils'; const closestButton = document.getElementById('submit-btn') expect(closestButton).toBeInTheDocument(); diff --git a/tests/lib/rules/prefer-find-by.test.ts b/tests/lib/rules/prefer-find-by.test.ts index 3bf6bed7..2f27043e 100644 --- a/tests/lib/rules/prefer-find-by.test.ts +++ b/tests/lib/rules/prefer-find-by.test.ts @@ -40,7 +40,7 @@ ruleTester.run(RULE_NAME, rule, { ...ASYNC_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` it('tests', async () => { - const { ${queryMethod} } = setup() + const { ${queryMethod} } = setup() // implicitly using ${testingFramework} const submitButton = await ${queryMethod}('foo') }) `, @@ -251,7 +251,7 @@ ruleTester.run(RULE_NAME, rule, { }, // invalid code, as we need findBy* to be defined somewhere, but required for getting 100% coverage { - code: `const submitButton = await waitFor(() => getByText('baz', { name: 'button' }))`, + code: `const submitButton = await waitFor(() => getByText('baz', { name: 'button' })) // implicitly using ${testingFramework}`, errors: [ { messageId: 'preferFindBy', @@ -263,12 +263,12 @@ ruleTester.run(RULE_NAME, rule, { }, }, ], - output: `const submitButton = await findByText('baz', { name: 'button' })`, + output: `const submitButton = await findByText('baz', { name: 'button' }) // implicitly using ${testingFramework}`, }, // this code would be invalid too, as findByRole is not defined anywhere. { code: ` - const getByRole = render().getByRole + const getByRole = render().getByRole // implicitly using ${testingFramework} const submitButton = await waitFor(() => getByRole('baz', { name: 'button' })) `, errors: [ @@ -283,7 +283,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], output: ` - const getByRole = render().getByRole + const getByRole = render().getByRole // implicitly using ${testingFramework} const submitButton = await findByRole('baz', { name: 'button' }) `, }, @@ -306,13 +306,7 @@ ruleTester.run(RULE_NAME, rule, { }, }, ], - output: ` - import { waitFor, render} from '${testingFramework}'; - it('tests', async () => { - const { getByCustomQuery } = render() - const submitButton = await waitFor(() => getByCustomQuery('baz')) - }) - `, + output: null, }, // custom query triggers the error but there is no fix - so output is the same { @@ -333,13 +327,7 @@ ruleTester.run(RULE_NAME, rule, { }, }, ], - output: ` - import {waitFor,render,screen} from '${testingFramework}'; - it('tests', async () => { - const { getByCustomQuery } = render() - const submitButton = await waitFor(() => screen.getByCustomQuery('baz')) - }) - `, + output: null, }, // presence matchers ...createScenario((waitMethod: string, queryMethod: string) => ({ diff --git a/tests/lib/rules/prefer-query-matchers.test.ts b/tests/lib/rules/prefer-query-matchers.test.ts index e16cf983..908f1b27 100644 --- a/tests/lib/rules/prefer-query-matchers.test.ts +++ b/tests/lib/rules/prefer-query-matchers.test.ts @@ -24,6 +24,7 @@ type AssertionFnParams = { query: string; matcher: string; options: Options; + onlyOptions?: boolean; }; const wrapExpectInTest = (expectStatement: string) => ` @@ -39,28 +40,37 @@ const getValidAssertions = ({ query, matcher, options, + onlyOptions = false, }: AssertionFnParams): RuleValidTestCase[] => { const expectStatement = `expect(${query}('Hello'))${matcher}`; const expectScreenStatement = `expect(screen.${query}('Hello'))${matcher}`; - return [ - { - // name: `${expectStatement} with default options of empty validEntries`, - code: wrapExpectInTest(expectStatement), - }, + const casesWithOptions = [ { // name: `${expectStatement} with provided options`, code: wrapExpectInTest(expectStatement), options, }, { - // name: `${expectScreenStatement} with default options of empty validEntries`, + // name: `${expectScreenStatement} with provided options`, code: wrapExpectInTest(expectScreenStatement), + options, }, + ]; + + if (onlyOptions) { + return casesWithOptions; + } + + return [ { - // name: `${expectScreenStatement} with provided options`, + // name: `${expectStatement} with default options of empty validEntries`, + code: wrapExpectInTest(expectStatement), + }, + { + // name: `${expectScreenStatement} with default options of empty validEntries`, code: wrapExpectInTest(expectScreenStatement), - options, }, + ...casesWithOptions, ]; }; @@ -150,6 +160,7 @@ ruleTester.run(RULE_NAME, rule, { options: [ { validEntries: [{ query: 'query', matcher: 'toBeVisible' }] }, ], + onlyOptions: true, }), ], [] @@ -192,6 +203,7 @@ ruleTester.run(RULE_NAME, rule, { options: [ { validEntries: [{ query: 'query', matcher: 'toBeVisible' }] }, ], + onlyOptions: true, }), ], [] @@ -234,6 +246,7 @@ ruleTester.run(RULE_NAME, rule, { options: [ { validEntries: [{ query: 'get', matcher: 'toBeVisible' }] }, ], + onlyOptions: true, }), ], [] @@ -276,6 +289,7 @@ ruleTester.run(RULE_NAME, rule, { options: [ { validEntries: [{ query: 'get', matcher: 'toBeVisible' }] }, ], + onlyOptions: true, }), ], [] diff --git a/tests/lib/test-utils.ts b/tests/lib/test-utils.ts index c9640129..aed679c7 100644 --- a/tests/lib/test-utils.ts +++ b/tests/lib/test-utils.ts @@ -2,11 +2,13 @@ import { resolve } from 'path'; import { TSESLint } from '@typescript-eslint/utils'; +import { FlatCompatRuleTester } from './FlatCompatRuleTester'; + const DEFAULT_TEST_CASE_CONFIG = { filename: 'MyComponent.test.js', }; -class TestingLibraryRuleTester extends TSESLint.RuleTester { +class TestingLibraryRuleTester extends FlatCompatRuleTester { run>( ruleName: string, rule: TSESLint.RuleModule,