diff --git a/package.json b/package.json index 6ba3b29..e23c4a6 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,11 @@ "concurrently": "^8.2.2", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", + "eslint-plugin-jest-dom": "^5.1.0", "eslint-plugin-sonarjs": "^0.23.0", "eslint-plugin-svelte": "^2.35.1", "eslint-plugin-tailwindcss": "^3.14.0", + "eslint-plugin-testing-library": "^6.2.0", "eslint-plugin-unicorn": "^50.0.1", "eslint-plugin-vitest": "^0.3.20", "prettier": "^3.2.4", diff --git a/packages/eslint-config/README.md b/packages/eslint-config/README.md index 3c21e42..211a1f6 100644 --- a/packages/eslint-config/README.md +++ b/packages/eslint-config/README.md @@ -45,9 +45,11 @@ pnpm add --save-dev \ @typescript-eslint/eslint-plugin \ eslint \ eslint-config-prettier \ + eslint-plugin-jest-dom \ eslint-plugin-sonarjs \ eslint-plugin-svelte \ eslint-plugin-tailwindcss \ + eslint-plugin-testing-library \ eslint-plugin-unicorn \ eslint-plugin-vitest ``` diff --git a/packages/eslint-config/base.cjs b/packages/eslint-config/base.cjs index 480bc00..1cdcd33 100644 --- a/packages/eslint-config/base.cjs +++ b/packages/eslint-config/base.cjs @@ -210,6 +210,7 @@ module.exports = { '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', 'sonarjs/no-duplicate-string': 'off', + 'unicorn/consistent-function-scoping': 'off', 'vitest/consistent-test-filename': [ 'error', { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 918cfca..64c5da5 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "0.3.2", + "version": "0.4.0", "description": "Common ESLint configuration for Viam projects.", "files": [ "**/*", @@ -45,19 +45,27 @@ "@typescript-eslint/parser": ">=6 <7", "eslint": ">=8 <9", "eslint-config-prettier": ">=9 <10", + "eslint-plugin-jest-dom": ">=5 <6", "eslint-plugin-sonarjs": ">=0.19 <0.24", "eslint-plugin-svelte": ">=2 <3", "eslint-plugin-tailwindcss": ">=3 <4", + "eslint-plugin-testing-library": ">=6 <7", "eslint-plugin-unicorn": ">=47 <51", "eslint-plugin-vitest": ">=0.3 <0.4" }, "peerDependenciesMeta": { + "eslint-plugin-jest-dom": { + "optional": true + }, "eslint-plugin-svelte": { "optional": true }, "eslint-plugin-tailwindcss": { "optional": true }, + "eslint-plugin-testing-library": { + "optional": true + }, "eslint-plugin-vitest": { "optional": true } diff --git a/packages/eslint-config/svelte.cjs b/packages/eslint-config/svelte.cjs index 5725e8b..4597fa2 100644 --- a/packages/eslint-config/svelte.cjs +++ b/packages/eslint-config/svelte.cjs @@ -39,5 +39,30 @@ module.exports = { 'no-undef-init': 'off', }, }, + // Rules for tests + { + files: ['**/__tests__/**', '**/*.test.ts', '**/*.spec.ts'], + extends: [ + 'plugin:vitest/recommended', + 'plugin:jest-dom/recommended', + 'plugin:testing-library/dom', + ], + rules: { + 'testing-library/await-async-events': [ + 'error', + { eventModule: ['fireEvent', 'userEvent'] }, + ], + 'testing-library/no-await-sync-events': 'off', + 'testing-library/no-node-access': [ + 'error', + { allowContainerFirstChild: true }, + ], + 'testing-library/prefer-explicit-assert': [ + 'error', + { assertion: 'toBeInTheDocument' }, + ], + 'testing-library/prefer-user-event': 'error', + }, + }, ], }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d238e3..241a628 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,6 +39,9 @@ importers: eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@8.56.0) + eslint-plugin-jest-dom: + specifier: ^5.1.0 + version: 5.1.0(eslint@8.56.0) eslint-plugin-sonarjs: specifier: ^0.23.0 version: 0.23.0(eslint@8.56.0) @@ -48,6 +51,9 @@ importers: eslint-plugin-tailwindcss: specifier: ^3.14.0 version: 3.14.0 + eslint-plugin-testing-library: + specifier: ^6.2.0 + version: 6.2.0(eslint@8.56.0)(typescript@5.3.3) eslint-plugin-unicorn: specifier: ^50.0.1 version: 50.0.1(eslint@8.56.0) @@ -591,6 +597,14 @@ packages: - supports-color dev: true + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + dev: true + /@typescript-eslint/scope-manager@6.17.0: resolution: {integrity: sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -627,6 +641,11 @@ packages: - supports-color dev: true + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@typescript-eslint/types@6.17.0: resolution: {integrity: sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==} engines: {node: ^16.0.0 || >=18.0.0} @@ -637,6 +656,27 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.3.3): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree@6.17.0(typescript@5.3.3): resolution: {integrity: sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -681,6 +721,26 @@ packages: - supports-color dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + eslint: 8.56.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/utils@6.17.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==} engines: {node: ^16.0.0 || >=18.0.0} @@ -719,6 +779,14 @@ packages: - typescript dev: true + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@typescript-eslint/visitor-keys@6.17.0: resolution: {integrity: sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1149,6 +1217,21 @@ packages: eslint: 8.56.0 dev: true + /eslint-plugin-jest-dom@5.1.0(eslint@8.56.0): + resolution: {integrity: sha512-JIXZp+E/h/aGlP/rQc4tuOejiHlZXg65qw8JAJMIJA5VsdjOkss/SYcRSqBrQuEOytEM8JvngUjcz31d1RrCrA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6', yarn: '>=1'} + peerDependencies: + '@testing-library/dom': ^8.0.0 || ^9.0.0 + eslint: ^6.8.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@testing-library/dom': + optional: true + dependencies: + '@babel/runtime': 7.23.2 + eslint: 8.56.0 + requireindex: 1.2.0 + dev: true + /eslint-plugin-sonarjs@0.23.0(eslint@8.56.0): resolution: {integrity: sha512-z44T3PBf9W7qQ/aR+NmofOTyg6HLhSEZOPD4zhStqBpLoMp8GYhFksuUBnCxbnf1nfISpKBVkQhiBLFI/F4Wlg==} engines: {node: '>=14'} @@ -1199,6 +1282,19 @@ packages: postcss: 8.4.33 dev: true + /eslint-plugin-testing-library@6.2.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-+LCYJU81WF2yQ+Xu4A135CgK8IszcFcyMF4sWkbiu6Oj+Nel0TrkZq/HvDw0/1WuO3dhDQsZA/OpEMGd0NfcUw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0, npm: '>=6'} + peerDependencies: + eslint: ^7.5.0 || ^8.0.0 + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /eslint-plugin-unicorn@50.0.1(eslint@8.56.0): resolution: {integrity: sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==} engines: {node: '>=16'} @@ -1248,6 +1344,14 @@ packages: - typescript dev: true + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1331,6 +1435,11 @@ packages: estraverse: 5.3.0 dev: true + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + /estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} @@ -2167,6 +2276,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /requireindex@1.2.0: + resolution: {integrity: sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==} + engines: {node: '>=0.10.5'} + dev: true + /resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -2430,10 +2544,24 @@ packages: typescript: 5.3.3 dev: true + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + dev: true + /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: true + /tsutils@3.21.0(typescript@5.3.3): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.3.3 + dev: true + /type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'}