From eed2d2d03181137b2908072d2c0d2d2178994790 Mon Sep 17 00:00:00 2001 From: Jozef Izso Date: Mon, 2 Mar 2026 17:15:02 +0100 Subject: [PATCH] Modernize ESLint configuration --- .eslintrc.json | 68 ------------------- __tests__/report/get-report.test.ts | 29 +++++--- eslint.config.mjs | 48 ++++++++++--- src/parsers/dotnet-trx/dotnet-trx-parser.ts | 9 ++- src/parsers/golang-json/golang-json-parser.ts | 39 ++++++----- src/parsers/golang-json/golang-json-types.ts | 10 +-- .../phpunit-junit/phpunit-junit-parser.ts | 2 +- .../tester-junit/tester-junit-parser.ts | 2 +- 8 files changed, 86 insertions(+), 121 deletions(-) delete mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 48214c7..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "plugins": ["import", "jest", "@typescript-eslint"], - "extends": ["plugin:github/recommended"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 9, - "sourceType": "module", - "project": "./tsconfig.json" - }, - "rules": { - "i18n-text/no-en": "off", - "eslint-comments/no-use": "off", - "import/no-namespace": "off", - "import/no-named-as-default": "off", - "no-shadow": "off", - "no-unused-vars": "off", - "prefer-template": "off", - "@typescript-eslint/no-unused-vars": ["error", {"varsIgnorePattern": "^_"}], - "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}], - "@typescript-eslint/no-require-imports": "error", - "@typescript-eslint/array-type": "error", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/ban-ts-comment": "error", - "camelcase": "off", - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], - "@typescript-eslint/func-call-spacing": ["error", "never"], - "@typescript-eslint/no-array-constructor": "error", - "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-extraneous-class": "error", - "@typescript-eslint/no-for-in-array": "error", - "@typescript-eslint/no-inferrable-types": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-assertion": "warn", - "@typescript-eslint/no-unnecessary-qualifier": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-useless-constructor": "error", - "@typescript-eslint/no-var-requires": "error", - "@typescript-eslint/prefer-for-of": "warn", - "@typescript-eslint/prefer-function-type": "warn", - "@typescript-eslint/prefer-includes": "error", - "@typescript-eslint/prefer-string-starts-ends-with": "error", - "@typescript-eslint/promise-function-async": "error", - "@typescript-eslint/require-array-sort-compare": "error", - "@typescript-eslint/restrict-plus-operands": "error", - "semi": "off", - "@typescript-eslint/semi": ["error", "never"], - "@typescript-eslint/type-annotation-spacing": "error", - "@typescript-eslint/unbound-method": "error" - }, - "env": { - "node": true, - "es6": true, - "jest/globals": true - }, - "settings": { - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "typescript": { - "alwaysTryTypes": true // always try to resolve types under `@types` directory even it doesn't contain any source code, like `@types/unist` - } - } - } -} diff --git a/__tests__/report/get-report.test.ts b/__tests__/report/get-report.test.ts index bc4a8b4..30e1e89 100644 --- a/__tests__/report/get-report.test.ts +++ b/__tests__/report/get-report.test.ts @@ -8,7 +8,9 @@ describe('getBadge', () => { badgeTitle: 'tests' } const badge = getBadge(5, 0, 1, options) - expect(badge).toBe('![Tests passed successfully](https://img.shields.io/badge/tests-5%20passed%2C%201%20skipped-success)') + expect(badge).toBe( + '![Tests passed successfully](https://img.shields.io/badge/tests-5%20passed%2C%201%20skipped-success)' + ) }) it('handles badge title with single hyphen', () => { @@ -28,7 +30,9 @@ describe('getBadge', () => { } const badge = getBadge(10, 0, 0, options) // All hyphens in the title should be encoded as -- - expect(badge).toBe('![Tests passed successfully](https://img.shields.io/badge/integration--api--tests-10%20passed-success)') + expect(badge).toBe( + '![Tests passed successfully](https://img.shields.io/badge/integration--api--tests-10%20passed-success)' + ) }) it('handles badge title with multiple underscores', () => { @@ -38,7 +42,9 @@ describe('getBadge', () => { } const badge = getBadge(10, 0, 0, options) // All underscores in the title should be encoded as __ - expect(badge).toBe('![Tests passed successfully](https://img.shields.io/badge/my__integration__test-10%20passed-success)') + expect(badge).toBe( + '![Tests passed successfully](https://img.shields.io/badge/my__integration__test-10%20passed-success)' + ) }) it('handles badge title with version format containing hyphen', () => { @@ -48,7 +54,9 @@ describe('getBadge', () => { } const badge = getBadge(1, 0, 0, options) // The hyphen in "12.0-ubi" should be encoded as -- - expect(badge).toBe('![Tests passed successfully](https://img.shields.io/badge/MariaDb%2012.0--ubi%20database%20tests-1%20passed-success)') + expect(badge).toBe( + '![Tests passed successfully](https://img.shields.io/badge/MariaDb%2012.0--ubi%20database%20tests-1%20passed-success)' + ) }) it('handles badge title with dots and hyphens', () => { @@ -57,7 +65,9 @@ describe('getBadge', () => { badgeTitle: 'v1.2.3-beta-test' } const badge = getBadge(4, 1, 0, options) - expect(badge).toBe('![Tests failed](https://img.shields.io/badge/v1.2.3--beta--test-4%20passed%2C%201%20failed-critical)') + expect(badge).toBe( + '![Tests failed](https://img.shields.io/badge/v1.2.3--beta--test-4%20passed%2C%201%20failed-critical)' + ) }) it('preserves structural hyphens between label and message', () => { @@ -67,7 +77,9 @@ describe('getBadge', () => { } const badge = getBadge(2, 3, 1, options) // The URI should have literal hyphens separating title-message-color - expect(badge).toBe('![Tests failed](https://img.shields.io/badge/test--suite-2%20passed%2C%203%20failed%2C%201%20skipped-critical)') + expect(badge).toBe( + '![Tests failed](https://img.shields.io/badge/test--suite-2%20passed%2C%203%20failed%2C%201%20skipped-critical)' + ) }) }) @@ -107,7 +119,9 @@ describe('getBadge', () => { it('includes passed, failed and skipped counts', () => { const options: ReportOptions = {...DEFAULT_OPTIONS} const badge = getBadge(5, 2, 1, options) - expect(badge).toBe('![Tests failed](https://img.shields.io/badge/tests-5%20passed%2C%202%20failed%2C%201%20skipped-critical)') + expect(badge).toBe( + '![Tests failed](https://img.shields.io/badge/tests-5%20passed%2C%202%20failed%2C%201%20skipped-critical)' + ) }) it('uses "none" message when no tests', () => { @@ -117,4 +131,3 @@ describe('getBadge', () => { }) }) }) - diff --git a/eslint.config.mjs b/eslint.config.mjs index 90c59bd..c30e115 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,32 +4,58 @@ import jest from 'eslint-plugin-jest' export default [ github.getFlatConfigs().recommended, ...github.getFlatConfigs().typescript, + { + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'] + }, + 'import/resolver': { + typescript: { + project: './tsconfig.json', + alwaysTryTypes: true + }, + node: { + extensions: ['.js', '.mjs', '.ts', '.tsx'] + } + } + } + }, { files: ['src/**/*.ts'], rules: { - 'no-shadow': 'off', - 'import/no-namespace': 'off', 'i18n-text/no-en': 'off', + 'eslint-comments/no-use': 'off', + 'import/no-namespace': 'off', + 'import/no-named-as-default': 'off', + 'no-shadow': 'off', + 'no-unused-vars': 'off', 'prefer-template': 'off', - "@typescript-eslint/array-type": ['error', {default: 'array'}], + camelcase: 'off', + semi: 'off', + '@typescript-eslint/array-type': ['error', {default: 'array'}], '@typescript-eslint/no-unused-vars': ['error', {varsIgnorePattern: '^_'}], '@typescript-eslint/no-shadow': ['error'], - }, + // Modern replacements for deprecated rules from the legacy config. + '@typescript-eslint/no-empty-object-type': 'error', + '@typescript-eslint/no-require-imports': 'error' + } }, { files: ['__tests__/**/*.test.ts'], ...jest.configs['flat/recommended'], plugins: { - jest, + jest }, languageOptions: { - globals: jest.environments.globals.globals, + globals: jest.environments.globals.globals }, rules: { 'i18n-text/no-en': 'off', 'import/no-namespace': 'off', - "@typescript-eslint/array-type": ['error', {default: 'array'}], - }, + '@typescript-eslint/array-type': ['error', {default: 'array'}], + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-function-return-type': 'off' + } }, { ignores: [ @@ -41,7 +67,7 @@ export default [ 'assets/**', 'reports/**', 'eslint.config.mjs', - 'jest.config.cjs', - ], - }, + 'jest.config.cjs' + ] + } ] diff --git a/src/parsers/dotnet-trx/dotnet-trx-parser.ts b/src/parsers/dotnet-trx/dotnet-trx-parser.ts index 9ffdd40..c5e93ce 100644 --- a/src/parsers/dotnet-trx/dotnet-trx-parser.ts +++ b/src/parsers/dotnet-trx/dotnet-trx-parser.ts @@ -62,8 +62,11 @@ export class DotnetTrxParser implements TestParser { } private getTestClasses(trx: TrxReport): TestClass[] { - if (trx.TestRun.TestDefinitions === undefined || trx.TestRun.Results === undefined || - !trx.TestRun.TestDefinitions.some(td => td.UnitTest && Array.isArray(td.UnitTest))) { + if ( + trx.TestRun.TestDefinitions === undefined || + trx.TestRun.Results === undefined || + !trx.TestRun.TestDefinitions.some(td => td.UnitTest && Array.isArray(td.UnitTest)) + ) { return [] } @@ -81,7 +84,7 @@ export class DotnetTrxParser implements TestParser { const testClasses: {[name: string]: TestClass} = {} for (const r of unitTestsResults) { - const className = r.test.TestMethod[0].$.className ?? "Unclassified" + const className = r.test.TestMethod[0].$.className ?? 'Unclassified' let tc = testClasses[className] if (tc === undefined) { tc = new TestClass(className) diff --git a/src/parsers/golang-json/golang-json-parser.ts b/src/parsers/golang-json/golang-json-parser.ts index afa8041..483c54e 100644 --- a/src/parsers/golang-json/golang-json-parser.ts +++ b/src/parsers/golang-json/golang-json-parser.ts @@ -1,8 +1,6 @@ -import { ParseOptions, TestParser } from '../../test-parser.js' +import {ParseOptions, TestParser} from '../../test-parser.js' -import { GoTestEvent } from './golang-json-types.js' -import { getExceptionSource } from '../../utils/node-utils.js' -import { getBasePath, normalizeFilePath } from '../../utils/path-utils.js' +import {GoTestEvent} from './golang-json-types.js' import { TestExecutionResult, @@ -16,7 +14,7 @@ import { export class GolangJsonParser implements TestParser { assumedWorkDir: string | undefined - constructor(readonly options: ParseOptions) { } + constructor(readonly options: ParseOptions) {} async parse(path: string, content: string): Promise { const events = await this.getGolangTestEvents(path, content) @@ -24,13 +22,16 @@ export class GolangJsonParser implements TestParser { } private async getGolangTestEvents(path: string, content: string): Promise { - return content.trim().split('\n').map((line, index) => { - try { - return JSON.parse(line) as GoTestEvent - } catch (e) { - throw new Error(`Invalid JSON at ${path} line ${index + 1}\n\n${e}`) - } - }) + return content + .trim() + .split('\n') + .map((line, index) => { + try { + return JSON.parse(line) as GoTestEvent + } catch (e) { + throw new Error(`Invalid JSON at ${path} line ${index + 1}\n\n${e}`) + } + }) } private getTestRunResult(path: string, events: GoTestEvent[]): TestRunResult { @@ -63,9 +64,8 @@ export class GolangJsonParser implements TestParser { continue } - let groupName: string | null - let rest: string[] - [groupName, ...rest] = event.Test.split('/') + const [first, ...rest] = event.Test.split('/') + let groupName: string | null = first let testName = rest.join('/') if (!testName) { testName = groupName @@ -80,9 +80,8 @@ export class GolangJsonParser implements TestParser { const lastEvent = eventGroup.at(-1)! - const result: TestExecutionResult = lastEvent.Action === 'pass' ? 'success' - : lastEvent.Action === 'skip' ? 'skipped' - : 'failed' + const result: TestExecutionResult = + lastEvent.Action === 'pass' ? 'success' : lastEvent.Action === 'skip' ? 'skipped' : 'failed' if (lastEvent.Elapsed === undefined) { throw new Error('missing elapsed on final test event') } @@ -94,7 +93,7 @@ export class GolangJsonParser implements TestParser { .filter(e => e.Action === 'output') .map(e => e.Output ?? '') // Go output prepends indentation to help group tests - remove it - .map(o => o.replace(/^ /, '')) + .map(o => o.replace(/^ {4}/, '')) // First and last lines will be generic "test started" and "test finished" lines - remove them outputEvents.splice(0, 1) @@ -103,7 +102,7 @@ export class GolangJsonParser implements TestParser { const details = outputEvents.join('') error = { message: details, - details: details + details } } diff --git a/src/parsers/golang-json/golang-json-types.ts b/src/parsers/golang-json/golang-json-types.ts index f27fa86..d714dfe 100644 --- a/src/parsers/golang-json/golang-json-types.ts +++ b/src/parsers/golang-json/golang-json-types.ts @@ -1,12 +1,4 @@ -export type GoTestAction = 'start' - | 'run' - | 'pause' - | 'cont' - | 'pass' - | 'bench' - | 'fail' - | 'output' - | 'skip' +export type GoTestAction = 'start' | 'run' | 'pause' | 'cont' | 'pass' | 'bench' | 'fail' | 'output' | 'skip' export type GoTestEvent = { Time: string diff --git a/src/parsers/phpunit-junit/phpunit-junit-parser.ts b/src/parsers/phpunit-junit/phpunit-junit-parser.ts index 63a8db5..4886fc1 100644 --- a/src/parsers/phpunit-junit/phpunit-junit-parser.ts +++ b/src/parsers/phpunit-junit/phpunit-junit-parser.ts @@ -130,7 +130,7 @@ export class PhpunitJunitParser implements TestParser { } const failure = failures[0] - const details = typeof failure === 'string' ? failure : failure._ ?? '' + const details = typeof failure === 'string' ? failure : (failure._ ?? '') // PHPUnit provides file path directly in testcase attributes let filePath: string | undefined diff --git a/src/parsers/tester-junit/tester-junit-parser.ts b/src/parsers/tester-junit/tester-junit-parser.ts index b4b97e0..47b7006 100644 --- a/src/parsers/tester-junit/tester-junit-parser.ts +++ b/src/parsers/tester-junit/tester-junit-parser.ts @@ -199,7 +199,7 @@ export class NetteTesterJunitParser implements TestParser { const failure = failures[0] // For Nette Tester, details are in the message attribute, not as inner text - const details = typeof failure === 'string' ? failure : failure._ ?? failure.$?.message ?? '' + const details = typeof failure === 'string' ? failure : (failure._ ?? failure.$?.message ?? '') // Try to extract file path and line from error details let errorFilePath: string | undefined