diff --git a/libraries/eslint-plugin/package.json b/libraries/eslint-plugin/package.json index 775f3e6e37..ecafc779e0 100644 --- a/libraries/eslint-plugin/package.json +++ b/libraries/eslint-plugin/package.json @@ -9,12 +9,19 @@ }, "devDependencies": { "@eslint/json": "^2.0.0", - "@typescript-eslint/parser": "^8.59.4" + "@typescript-eslint/parser": "^8.59.4", + "typescript": "^5.0.4", + "vitest": "4.1.5" }, "peerDependencies": { "eslint": "^10.4.0" }, "scripts": { - "test": "node rules.test.js" + "test": "yarn run lint && yarn run types:check && yarn run test:unit", + "lint": "eslint --cache --cache-location ../../.cache/eslint/ --ext .cjs,.js,.jsx,.mjs,.ts --max-warnings 0 --format unix .", + "lint:fix": "eslint --cache --cache-location ../../.cache/eslint/ --fix --ext .cjs,.js,.jsx,.mjs,.ts .", + "test:ci": "yarn run test:unit", + "test:unit": "vitest --config vitest.config.ts", + "types:check": "tsc --noEmit" } } diff --git a/libraries/eslint-plugin/prefer-kebab-url.js b/libraries/eslint-plugin/prefer-kebab-url.js index 9c7b834dca..ab4c203b26 100644 --- a/libraries/eslint-plugin/prefer-kebab-url.js +++ b/libraries/eslint-plugin/prefer-kebab-url.js @@ -29,7 +29,7 @@ const getSuggestion = routePath => { if (routePath instanceof RegExp) { const words = removeTextBetweenBrackets(routePath.source).match(/[\w-]+/g) - if (!words) return routePath + if (!words) return null let newSource = routePath.source for (const word of words) { @@ -42,9 +42,7 @@ const getSuggestion = routePath => { } const kebabed = new RegExp(newSource, routePath.flags) - return kebabed.source.toString() === routePath.source.toString() - ? null - : kebabed + return kebabed.source === routePath.source ? null : kebabed.toString() } } diff --git a/libraries/eslint-plugin/test/setup.js b/libraries/eslint-plugin/test/setup.js new file mode 100644 index 0000000000..ed2eaf4fe6 --- /dev/null +++ b/libraries/eslint-plugin/test/setup.js @@ -0,0 +1,9 @@ +import { afterAll, describe, it } from 'vitest' +import { RuleTester } from 'eslint' + +// ESLint's RuleTester discovers the test framework via these hooks. The repo's +// vitest configs deliberately avoid `globals: true`, so wire them explicitly. +RuleTester.afterAll = afterAll +RuleTester.describe = describe +RuleTester.it = it +RuleTester.itOnly = it.only diff --git a/libraries/eslint-plugin/rules.test.js b/libraries/eslint-plugin/test/unit/rules.test.js similarity index 89% rename from libraries/eslint-plugin/rules.test.js rename to libraries/eslint-plugin/test/unit/rules.test.js index 02178362be..0d1158ffa5 100644 --- a/libraries/eslint-plugin/rules.test.js +++ b/libraries/eslint-plugin/test/unit/rules.test.js @@ -1,18 +1,21 @@ -const { RuleTester } = require('eslint') -const tsParser = require('@typescript-eslint/parser') -const json = require('@eslint/json').default -const noThrowInCallback = require('./no-throw-in-callback') -const preferKebabUrl = require('./prefer-kebab-url') -const noUnnecessaryTrans = require('./no-unnecessary-trans') -const shouldUnescapeTrans = require('./should-unescape-trans') -const noGeneratedEditorThemes = require('./no-generated-editor-themes') -const viDoMockValidPath = require('./require-vi-doMock-valid-path') -const requireCioSnakeCaseProperties = require('./require-cio-snake-case-properties') -const requireRelForDocsLinks = require('./require-rel-for-docs-links') -const noConsecutiveSpacesInLocales = require('./no-consecutive-spaces-in-locales') -const noStraightApostrophesInLocales = require('./no-straight-apostrophes-in-locales') -const frenchTypographyInLocales = require('./french-typography-in-locales') -const sortedKeysInLocales = require('./sorted-keys-in-locales') +import { fileURLToPath } from 'node:url' +import { RuleTester } from 'eslint' +import tsParser from '@typescript-eslint/parser' +import json from '@eslint/json' +import noThrowInCallback from '../../no-throw-in-callback.js' +import preferKebabUrl from '../../prefer-kebab-url.js' +import noUnnecessaryTrans from '../../no-unnecessary-trans.js' +import shouldUnescapeTrans from '../../should-unescape-trans.js' +import noGeneratedEditorThemes from '../../no-generated-editor-themes.js' +import viDoMockValidPath from '../../require-vi-doMock-valid-path.js' +import requireCioSnakeCaseProperties from '../../require-cio-snake-case-properties.js' +import requireRelForDocsLinks from '../../require-rel-for-docs-links.js' +import noConsecutiveSpacesInLocales from '../../no-consecutive-spaces-in-locales.js' +import noStraightApostrophesInLocales from '../../no-straight-apostrophes-in-locales.js' +import frenchTypographyInLocales from '../../french-typography-in-locales.js' +import sortedKeysInLocales from '../../sorted-keys-in-locales.js' + +const __filename = fileURLToPath(import.meta.url) const ruleTester = new RuleTester({ languageOptions: { @@ -64,7 +67,15 @@ ruleTester.run('prefer-kebab-url', preferKebabUrl, { { code: `router.get(/^\\/downLoad\\/pro-ject\\/([^/]*)\\/OutPut\\/out-put\\.pdf$/)`, errors: [ - { message: 'Route path should be in kebab-case.', suggestions: 1 }, + { + message: 'Route path should be in kebab-case.', + suggestions: [ + { + desc: 'Change to kebab-case: /^\\/down-load\\/pro-ject\\/([^/]*)\\/out-put\\/out-put\\.pdf$/', + output: `router.get(/^\\/down-load\\/pro-ject\\/([^/]*)\\/out-put\\/out-put\\.pdf$/)`, + }, + ], + }, ], }, ], @@ -157,28 +168,28 @@ ruleTester.run('no-generated-editor-themes', noGeneratedEditorThemes, { ruleTester.run('domock-require-valid-path', viDoMockValidPath, { valid: [ { - code: 'vi.doMock("./require-vi-doMock-valid-path.js")', + code: 'vi.doMock("../../require-vi-doMock-valid-path.js")', filename: __filename, }, { - code: 'const filename = "./require-vi-doMock-valid-path.js"; vi.doMock(filename);', + code: 'const filename = "../../require-vi-doMock-valid-path.js"; vi.doMock(filename);', filename: __filename, }, ], invalid: [ { - code: "vi.doMock('./require-vi-doMock-valid-path2')", + code: "vi.doMock('../../require-vi-doMock-valid-path2')", filename: __filename, errors: [ { message: - 'The path "./require-vi-doMock-valid-path2" in vi.doMock() cannot be resolved relative to the current file.', + 'The path "../../require-vi-doMock-valid-path2" in vi.doMock() cannot be resolved relative to the current file.', suggestions: [], }, ], }, { - code: 'const filename = "./require-vi-doMock-valid-path2.js"; vi.doMock(filename);', + code: 'const filename = "../../require-vi-doMock-valid-path2.js"; vi.doMock(filename);', filename: __filename, errors: [ { diff --git a/libraries/eslint-plugin/tsconfig.json b/libraries/eslint-plugin/tsconfig.json index a8e4493a10..5c3833f9db 100644 --- a/libraries/eslint-plugin/tsconfig.json +++ b/libraries/eslint-plugin/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.backend.json", - "include": ["**/*.js"] + "include": ["**/*.js", "**/*.cjs", "**/*.ts"] } diff --git a/libraries/eslint-plugin/vitest.config.ts b/libraries/eslint-plugin/vitest.config.ts new file mode 100644 index 0000000000..c19ff9733a --- /dev/null +++ b/libraries/eslint-plugin/vitest.config.ts @@ -0,0 +1,25 @@ +import { defineConfig, ViteUserConfig } from 'vitest/config' + +let reporterOptions: ViteUserConfig['test'] = {} +if (process.env.CI) { + reporterOptions = { + reporters: [ + 'default', + [ + 'junit', + { + classnameTemplate: `Unit tests.{filename}`, + }, + ], + ], + outputFile: 'reports/junit-vitest-unit.xml', + } +} +export default defineConfig({ + test: { + include: ['test/unit/**/*.test.{js,ts}'], + setupFiles: ['./test/setup.js'], + isolate: false, + ...reporterOptions, + }, +}) diff --git a/yarn.lock b/yarn.lock index 473ca231cc..f088f6e877 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6843,6 +6843,8 @@ __metadata: "@eslint/json": "npm:^2.0.0" "@typescript-eslint/parser": "npm:^8.59.4" lodash: "npm:^4.18.1" + typescript: "npm:^5.0.4" + vitest: "npm:4.1.5" peerDependencies: eslint: ^10.4.0 languageName: unknown