From 5284994dd847b032bd006dd1765edc8902d3f874 Mon Sep 17 00:00:00 2001 From: StyleShit <32631382+StyleShit@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:55:21 +0200 Subject: [PATCH 1/2] build(lint): add vitest eslint rules [AR-47371] --- eslint.config.base.ts | 41 ++++++ package.json | 1 + packages/design-system/tests/exports.test.ts | 32 ++--- pnpm-lock.yaml | 127 ++++++++++++++++++- 4 files changed, 180 insertions(+), 21 deletions(-) diff --git a/eslint.config.base.ts b/eslint.config.base.ts index 8d521c73d..7bb601743 100644 --- a/eslint.config.base.ts +++ b/eslint.config.base.ts @@ -5,6 +5,7 @@ import tseslint from 'typescript-eslint'; import unicorn from 'eslint-plugin-unicorn'; import importX, { createNodeResolver } from 'eslint-plugin-import-x'; import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'; +import vitest from '@vitest/eslint-plugin'; import globals from 'globals'; export default defineConfig( @@ -128,5 +129,45 @@ export default defineConfig( }, }, + // Test rules. + { + files: ['**/*.@(test|spec).[tj]s?(x)'], + ...vitest.configs.recommended, + rules: { + ...vitest.configs.recommended.rules, + 'vitest/prefer-vi-mocked': 'error', + 'vitest/prefer-hooks-on-top': 'error', + 'vitest/no-duplicate-hooks': 'error', + 'vitest/hoisted-apis-on-top': 'error', + + 'vitest/consistent-each-for': [ + 'error', + { + test: 'each', + it: 'each', + describe: 'each', + suite: 'each', + }, + ], + + 'vitest/consistent-test-filename': [ + 'error', + { + pattern: '\\.test\\.[tj]sx?$', + }, + ], + + 'vitest/consistent-test-it': [ + 'error', + { + fn: 'it', + withinDescribe: 'it', + }, + ], + + 'vitest/prefer-hooks-in-order': 'error', + }, + }, + globalIgnores(['**/dist', '**/.turbo']), ); diff --git a/package.json b/package.json index 59ea7b53d..d47b7ab16 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@eslint/core": "^1.1.0", "@eslint/js": "^9.39.2", "@types/node": "^25.2.2", + "@vitest/eslint-plugin": "^1.6.7", "cspell": "^9.6.4", "eslint": "^9.39.2", "eslint-import-resolver-typescript": "^4.4.4", diff --git a/packages/design-system/tests/exports.test.ts b/packages/design-system/tests/exports.test.ts index 17e67abaf..a32f08ba2 100644 --- a/packages/design-system/tests/exports.test.ts +++ b/packages/design-system/tests/exports.test.ts @@ -2,24 +2,26 @@ import fs from 'node:fs'; import { describe, expect, it } from 'vitest'; describe('Design System exports', () => { - const expectedLines = fs - .readdirSync('./src/components') - .toSorted() - .map((component) => { - return `export * from './components/${component}';`; - }); + it('should export all components', () => { + const expectedLines = fs + .readdirSync('./src/components') + .toSorted() + .map((component) => { + return `export * from './components/${component}';`; + }); - const actualContent = fs.readFileSync('./src/index.ts', 'utf-8'); - const actualLines = actualContent.split('\n').filter((line) => line.trim().length > 0); + const actualContent = fs.readFileSync('./src/index.ts', 'utf-8'); + const actualLines = actualContent.split('\n').filter((line) => line.trim().length > 0); - expect(actualLines.length).toBe(expectedLines.length); + expect(actualLines.length).toBe(expectedLines.length); - const tests = expectedLines.map((expectedLine, index) => { - return [actualLines[index], expectedLine]; - }); + const tests = expectedLines.map((expectedLine, index) => { + return [actualLines[index], expectedLine]; + }); - // split expected by line and assert that each line is exported - it.each(tests)('"%s" is exported from the package', (actual, expected) => { - expect(actual).toBe(expected); + // split expected by line and assert that each line is exported + it.each(tests)('"%s" is exported from the package', (actual, expected) => { + expect(actual).toBe(expected); + }); }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f180470f7..5b66b3239 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: '@types/node': specifier: ^25.2.2 version: 25.2.2 + '@vitest/eslint-plugin': + specifier: ^1.6.7 + version: 1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18) cspell: specifier: ^9.6.4 version: 9.6.4 @@ -61,10 +64,10 @@ importers: version: 9.39.2(jiti@2.6.1) eslint-import-resolver-typescript: specifier: ^4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import-x: specifier: ^4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) + version: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-unicorn: specifier: ^62.0.0 version: 62.0.0(eslint@9.39.2(jiti@2.6.1)) @@ -2627,16 +2630,32 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/project-service@8.55.0': + resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/scope-manager@8.54.0': resolution: {integrity: sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.55.0': + resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.54.0': resolution: {integrity: sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/tsconfig-utils@8.55.0': + resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/type-utils@8.54.0': resolution: {integrity: sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2648,12 +2667,22 @@ packages: resolution: {integrity: sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.55.0': + resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.54.0': resolution: {integrity: sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/typescript-estree@8.55.0': + resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.54.0': resolution: {integrity: sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2661,10 +2690,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/utils@8.55.0': + resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/visitor-keys@8.54.0': resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.55.0': + resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} cpu: [arm] @@ -2780,6 +2820,19 @@ packages: '@vitest/browser': optional: true + '@vitest/eslint-plugin@1.6.7': + resolution: {integrity: sha512-sd2QJirEscSQk3Pywtelbs7z8RQp1gyF5BfeZVtTHE8y3suyzbAA71NuT9z01uTRMHoCf5p6M2t2WYNJ7m5FlA==} + engines: {node: '>=18'} + peerDependencies: + eslint: '>=8.57.0' + typescript: '>=5.0.0' + vitest: '*' + peerDependenciesMeta: + typescript: + optional: true + vitest: + optional: true + '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -8103,15 +8156,33 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.55.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.54.0': dependencies: '@typescript-eslint/types': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/scope-manager@8.55.0': + dependencies: + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 + '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 + '@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@typescript-eslint/types': 8.54.0 @@ -8126,6 +8197,8 @@ snapshots: '@typescript-eslint/types@8.54.0': {} + '@typescript-eslint/types@8.55.0': {} + '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) @@ -8141,6 +8214,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.55.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) @@ -8152,11 +8240,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.54.0': dependencies: '@typescript-eslint/types': 8.54.0 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.55.0': + dependencies: + '@typescript-eslint/types': 8.55.0 + eslint-visitor-keys: 4.2.1 + '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true @@ -8262,6 +8366,17 @@ snapshots: optionalDependencies: '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(less@4.5.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(yaml@2.8.2))(vitest@4.0.18) + '@vitest/eslint-plugin@1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18)': + dependencies: + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.2(jiti@2.6.1) + optionalDependencies: + typescript: 5.9.3 + vitest: 4.0.18(@types/node@25.2.2)(@vitest/browser-playwright@4.0.18)(jiti@2.6.1)(jsdom@25.0.1)(less@4.5.1)(sass-embedded@1.97.3)(sass@1.97.3)(stylus@0.62.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -9487,7 +9602,7 @@ snapshots: optionalDependencies: unrs-resolver: 1.11.1 - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) @@ -9498,7 +9613,7 @@ snapshots: tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -9510,7 +9625,7 @@ snapshots: - supports-color - typescript - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@typescript-eslint/types': 8.54.0 comment-parser: 1.4.5 @@ -9523,7 +9638,7 @@ snapshots: stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - supports-color From b2c2970aedab9d754945e2271d37b1558ef3ca5c Mon Sep 17 00:00:00 2001 From: StyleShit <32631382+StyleShit@users.noreply.github.com> Date: Tue, 10 Feb 2026 12:02:23 +0200 Subject: [PATCH 2/2] wip --- packages/design-system/tests/exports.test.ts | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/design-system/tests/exports.test.ts b/packages/design-system/tests/exports.test.ts index a32f08ba2..f07f6dfd8 100644 --- a/packages/design-system/tests/exports.test.ts +++ b/packages/design-system/tests/exports.test.ts @@ -2,26 +2,26 @@ import fs from 'node:fs'; import { describe, expect, it } from 'vitest'; describe('Design System exports', () => { - it('should export all components', () => { - const expectedLines = fs - .readdirSync('./src/components') - .toSorted() - .map((component) => { - return `export * from './components/${component}';`; - }); + const expectedLines = fs + .readdirSync('./src/components') + .toSorted() + .map((component) => { + return `export * from './components/${component}';`; + }); - const actualContent = fs.readFileSync('./src/index.ts', 'utf-8'); - const actualLines = actualContent.split('\n').filter((line) => line.trim().length > 0); + const actualContent = fs.readFileSync('./src/index.ts', 'utf-8'); + const actualLines = actualContent.split('\n').filter((line) => line.trim().length > 0); - expect(actualLines.length).toBe(expectedLines.length); + const tests = expectedLines.map((expectedLine, index) => { + return [actualLines[index], expectedLine]; + }); - const tests = expectedLines.map((expectedLine, index) => { - return [actualLines[index], expectedLine]; - }); + it('should export all components', () => { + expect(actualLines.length).toBe(expectedLines.length); + }); - // split expected by line and assert that each line is exported - it.each(tests)('"%s" is exported from the package', (actual, expected) => { - expect(actual).toBe(expected); - }); + // split expected by line and assert that each line is exported + it.each(tests)('"%s" is exported from the package', (actual, expected) => { + expect(actual).toBe(expected); }); });