diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/accessor.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/accessor.test.ts new file mode 100644 index 000000000000..a2f9ff06a21d --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/accessor.test.ts @@ -0,0 +1,17 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'const ignored = { get %() {} };', + 'const ignored = { set "%"(ignored) {} };', + 'class Ignored { private get %() {} }', + 'class Ignored { private set "%"(ignored) {} }', + 'class Ignored { private static get %() {} }', + 'class Ignored { static get #%() {} }', + ], + options: { + selector: 'accessor', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/class.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/class.test.ts new file mode 100644 index 000000000000..81240701411b --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/class.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['class % {}', 'abstract class % {}', 'const ignored = class % {}'], + options: { + selector: 'class', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts new file mode 100644 index 000000000000..9d2abfa13383 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts @@ -0,0 +1,480 @@ +import { TSESLint } from '@typescript-eslint/utils'; +import rule, { + MessageIds, + Options, +} from '../../../../src/rules/naming-convention'; +import { + PredefinedFormatsString, + Selector, + selectorTypeToMessageString, +} from '../../../../src/rules/naming-convention-utils'; +import { RuleTester } from '../../../RuleTester'; + +export const formatTestNames: Readonly< + Record> +> = { + camelCase: { + valid: ['strictCamelCase', 'lower', 'camelCaseUNSTRICT'], + invalid: ['snake_case', 'UPPER_CASE', 'UPPER', 'StrictPascalCase'], + }, + strictCamelCase: { + valid: ['strictCamelCase', 'lower'], + invalid: [ + 'snake_case', + 'UPPER_CASE', + 'UPPER', + 'StrictPascalCase', + 'camelCaseUNSTRICT', + ], + }, + PascalCase: { + valid: [ + 'StrictPascalCase', + 'Pascal', + 'I18n', + 'PascalCaseUNSTRICT', + 'UPPER', + ], + invalid: ['snake_case', 'UPPER_CASE', 'strictCamelCase'], + }, + StrictPascalCase: { + valid: ['StrictPascalCase', 'Pascal', 'I18n'], + invalid: [ + 'snake_case', + 'UPPER_CASE', + 'UPPER', + 'strictCamelCase', + 'PascalCaseUNSTRICT', + ], + }, + UPPER_CASE: { + valid: ['UPPER_CASE', 'UPPER'], + invalid: [ + 'lower', + 'snake_case', + 'SNAKE_case_UNSTRICT', + 'strictCamelCase', + 'StrictPascalCase', + ], + }, + snake_case: { + valid: ['snake_case', 'lower'], + invalid: [ + 'UPPER_CASE', + 'SNAKE_case_UNSTRICT', + 'strictCamelCase', + 'StrictPascalCase', + ], + }, +}; + +const REPLACE_REGEX = /%/g; +// filter to not match `[iI]gnored` +const IGNORED_FILTER = { + match: false, + regex: /.gnored/.source, +}; + +type Cases = { + code: string[]; + options: Omit; +}[]; + +export function createTestCases(cases: Cases): void { + const ruleTester = new RuleTester({ + parser: '@typescript-eslint/parser', + }); + + ruleTester.run('naming-convention', rule, { + invalid: createInvalidTestCases(), + valid: createValidTestCases(), + }); + + function createValidTestCases(): TSESLint.ValidTestCase[] { + const newCases: TSESLint.ValidTestCase[] = []; + + for (const test of cases) { + for (const [formatLoose, names] of Object.entries(formatTestNames)) { + const format = [formatLoose as PredefinedFormatsString]; + for (const name of names.valid) { + const createCase = ( + preparedName: string, + options: Selector, + ): TSESLint.ValidTestCase => ({ + options: [ + { + ...options, + filter: IGNORED_FILTER, + }, + ], + code: `// ${JSON.stringify(options)}\n${test.code + .map(code => code.replace(REPLACE_REGEX, preparedName)) + .join('\n')}`, + }); + + newCases.push( + createCase(name, { + ...test.options, + format, + }), + + // leadingUnderscore + createCase(name, { + ...test.options, + format, + leadingUnderscore: 'forbid', + }), + createCase(`_${name}`, { + ...test.options, + format, + leadingUnderscore: 'require', + }), + createCase(`__${name}`, { + ...test.options, + format, + leadingUnderscore: 'requireDouble', + }), + createCase(`_${name}`, { + ...test.options, + format, + leadingUnderscore: 'allow', + }), + createCase(name, { + ...test.options, + format, + leadingUnderscore: 'allow', + }), + createCase(`__${name}`, { + ...test.options, + format, + leadingUnderscore: 'allowDouble', + }), + createCase(name, { + ...test.options, + format, + leadingUnderscore: 'allowDouble', + }), + createCase(`_${name}`, { + ...test.options, + format, + leadingUnderscore: 'allowSingleOrDouble', + }), + createCase(name, { + ...test.options, + format, + leadingUnderscore: 'allowSingleOrDouble', + }), + createCase(`__${name}`, { + ...test.options, + format, + leadingUnderscore: 'allowSingleOrDouble', + }), + createCase(name, { + ...test.options, + format, + leadingUnderscore: 'allowSingleOrDouble', + }), + + // trailingUnderscore + createCase(name, { + ...test.options, + format, + trailingUnderscore: 'forbid', + }), + createCase(`${name}_`, { + ...test.options, + format, + trailingUnderscore: 'require', + }), + createCase(`${name}__`, { + ...test.options, + format, + trailingUnderscore: 'requireDouble', + }), + createCase(`${name}_`, { + ...test.options, + format, + trailingUnderscore: 'allow', + }), + createCase(name, { + ...test.options, + format, + trailingUnderscore: 'allow', + }), + createCase(`${name}__`, { + ...test.options, + format, + trailingUnderscore: 'allowDouble', + }), + createCase(name, { + ...test.options, + format, + trailingUnderscore: 'allowDouble', + }), + createCase(`${name}_`, { + ...test.options, + format, + trailingUnderscore: 'allowSingleOrDouble', + }), + createCase(name, { + ...test.options, + format, + trailingUnderscore: 'allowSingleOrDouble', + }), + createCase(`${name}__`, { + ...test.options, + format, + trailingUnderscore: 'allowSingleOrDouble', + }), + createCase(name, { + ...test.options, + format, + trailingUnderscore: 'allowSingleOrDouble', + }), + + // prefix + createCase(`MyPrefix${name}`, { + ...test.options, + format, + prefix: ['MyPrefix'], + }), + createCase(`MyPrefix2${name}`, { + ...test.options, + format, + prefix: ['MyPrefix1', 'MyPrefix2'], + }), + + // suffix + createCase(`${name}MySuffix`, { + ...test.options, + format, + suffix: ['MySuffix'], + }), + createCase(`${name}MySuffix2`, { + ...test.options, + format, + suffix: ['MySuffix1', 'MySuffix2'], + }), + ); + } + } + } + + return newCases; + } + + function createInvalidTestCases(): TSESLint.InvalidTestCase< + MessageIds, + Options + >[] { + const newCases: TSESLint.InvalidTestCase[] = []; + + for (const test of cases) { + for (const [formatLoose, names] of Object.entries(formatTestNames)) { + const format = [formatLoose as PredefinedFormatsString]; + for (const name of names.invalid) { + const createCase = ( + preparedName: string, + options: Selector, + messageId: MessageIds, + data: Record = {}, + ): TSESLint.InvalidTestCase => { + const selectors = Array.isArray(test.options.selector) + ? test.options.selector + : [test.options.selector]; + const errorsTemplate = selectors.map(selector => ({ + messageId, + ...(selector !== 'default' && + selector !== 'variableLike' && + selector !== 'memberLike' && + selector !== 'typeLike' && + selector !== 'property' && + selector !== 'method' + ? { + data: { + type: selectorTypeToMessageString(selector), + name: preparedName, + ...data, + }, + } + : // meta-types will use the correct selector, so don't assert on data shape + {}), + })); + + const errors: { + data?: { type: string; name: string }; + messageId: MessageIds; + }[] = []; + test.code.forEach(() => errors.push(...errorsTemplate)); + + return { + options: [ + { + ...options, + filter: IGNORED_FILTER, + }, + ], + code: `// ${JSON.stringify(options)}\n${test.code + .map(code => code.replace(REPLACE_REGEX, preparedName)) + .join('\n')}`, + errors: errors, + }; + }; + + const prefixSingle = ['MyPrefix']; + const prefixMulti = ['MyPrefix1', 'MyPrefix2']; + const suffixSingle = ['MySuffix']; + const suffixMulti = ['MySuffix1', 'MySuffix2']; + + newCases.push( + createCase( + name, + { + ...test.options, + format, + }, + 'doesNotMatchFormat', + { formats: format.join(', ') }, + ), + + // leadingUnderscore + createCase( + `_${name}`, + { + ...test.options, + format, + leadingUnderscore: 'forbid', + }, + 'unexpectedUnderscore', + { position: 'leading' }, + ), + createCase( + name, + { + ...test.options, + format, + leadingUnderscore: 'require', + }, + 'missingUnderscore', + { position: 'leading', count: 'one' }, + ), + createCase( + name, + { + ...test.options, + format, + leadingUnderscore: 'requireDouble', + }, + 'missingUnderscore', + { position: 'leading', count: 'two' }, + ), + createCase( + `_${name}`, + { + ...test.options, + format, + leadingUnderscore: 'requireDouble', + }, + 'missingUnderscore', + { position: 'leading', count: 'two' }, + ), + + // trailingUnderscore + createCase( + `${name}_`, + { + ...test.options, + format, + trailingUnderscore: 'forbid', + }, + 'unexpectedUnderscore', + { position: 'trailing' }, + ), + createCase( + name, + { + ...test.options, + format, + trailingUnderscore: 'require', + }, + 'missingUnderscore', + { position: 'trailing', count: 'one' }, + ), + createCase( + name, + { + ...test.options, + format, + trailingUnderscore: 'requireDouble', + }, + 'missingUnderscore', + { position: 'trailing', count: 'two' }, + ), + createCase( + `${name}_`, + { + ...test.options, + format, + trailingUnderscore: 'requireDouble', + }, + 'missingUnderscore', + { position: 'trailing', count: 'two' }, + ), + + // prefix + createCase( + name, + { + ...test.options, + format, + prefix: prefixSingle, + }, + 'missingAffix', + { position: 'prefix', affixes: prefixSingle.join(', ') }, + ), + createCase( + name, + { + ...test.options, + format, + prefix: prefixMulti, + }, + 'missingAffix', + { + position: 'prefix', + affixes: prefixMulti.join(', '), + }, + ), + + // suffix + createCase( + name, + { + ...test.options, + format, + suffix: suffixSingle, + }, + 'missingAffix', + { position: 'suffix', affixes: suffixSingle.join(', ') }, + ), + createCase( + name, + { + ...test.options, + format, + suffix: suffixMulti, + }, + 'missingAffix', + { + position: 'suffix', + affixes: suffixMulti.join(', '), + }, + ), + ); + } + } + } + + return newCases; + } +} diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/default.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/default.test.ts new file mode 100644 index 000000000000..4a3459b8639c --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/default.test.ts @@ -0,0 +1,32 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'const % = 1;', + 'function % () {}', + '(function (%) {});', + 'class Ignored { constructor(private %) {} }', + 'const ignored = { % };', + 'interface Ignored { %: string }', + 'type Ignored = { %: string }', + 'class Ignored { private % = 1 }', + 'class Ignored { #% = 1 }', + 'class Ignored { constructor(private %) {} }', + 'class Ignored { #%() {} }', + 'class Ignored { private %() {} }', + 'const ignored = { %() {} };', + 'class Ignored { private get %() {} }', + 'enum Ignored { % }', + 'abstract class % {}', + 'interface % { }', + 'type % = { };', + 'enum % {}', + 'interface Ignored<%> extends Ignored {}', + ], + options: { + selector: 'default', + filter: '[iI]gnored', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/enum.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/enum.test.ts new file mode 100644 index 000000000000..46a3af946e18 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/enum.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['enum % {}'], + options: { + selector: 'enum', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/enumMember.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/enumMember.test.ts new file mode 100644 index 000000000000..a283b96788c0 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/enumMember.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['enum Ignored { % }', 'enum Ignored { "%" }'], + options: { + selector: 'enumMember', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/function.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/function.test.ts new file mode 100644 index 000000000000..6c68e1715a63 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/function.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['function % () {}', '(function % () {});', 'declare function % ();'], + options: { + selector: 'function', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/interface.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/interface.test.ts new file mode 100644 index 000000000000..dc31fcc8f19d --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/interface.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['interface % {}'], + options: { + selector: 'interface', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/method.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/method.test.ts new file mode 100644 index 000000000000..b4973f23297b --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/method.test.ts @@ -0,0 +1,42 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'class Ignored { private %() {} }', + 'class Ignored { private "%"() {} }', + 'class Ignored { private readonly %() {} }', + 'class Ignored { private static %() {} }', + 'class Ignored { private static readonly %() {} }', + 'class Ignored { private % = () => {} }', + 'class Ignored { abstract %() }', + 'class Ignored { declare %() }', + 'class Ignored { #%() }', + 'class Ignored { static #%() }', + ], + options: { + selector: 'classMethod', + }, + }, + { + code: [ + 'const ignored = { %() {} };', + 'const ignored = { "%"() {} };', + 'const ignored = { %: () => {} };', + ], + options: { + selector: 'objectLiteralMethod', + }, + }, + { + code: [ + 'interface Ignored { %(): string }', + 'interface Ignored { "%"(): string }', + 'type Ignored = { %(): string }', + 'type Ignored = { "%"(): string }', + ], + options: { + selector: 'typeMethod', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/parameter.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/parameter.test.ts new file mode 100644 index 000000000000..d82f58239b4f --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/parameter.test.ts @@ -0,0 +1,21 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'function ignored(%) {}', + '(function (%) {});', + 'declare function ignored(%);', + 'function ignored({%}) {}', + 'function ignored(...%) {}', + 'function ignored({% = 1}) {}', + 'function ignored({...%}) {}', + 'function ignored([%]) {}', + 'function ignored([% = 1]) {}', + 'function ignored([...%]) {}', + ], + options: { + selector: 'parameter', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/parameterProperty.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/parameterProperty.test.ts new file mode 100644 index 000000000000..146cb4dfb540 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/parameterProperty.test.ts @@ -0,0 +1,21 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'class Ignored { constructor(private %) {} }', + 'class Ignored { constructor(readonly %) {} }', + 'class Ignored { constructor(private readonly %) {} }', + ], + options: { + selector: 'parameterProperty', + }, + }, + { + code: ['class Ignored { constructor(private readonly %) {} }'], + options: { + selector: 'parameterProperty', + modifiers: ['readonly'], + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/property.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/property.test.ts new file mode 100644 index 000000000000..6d8fdf42608b --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/property.test.ts @@ -0,0 +1,37 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'class Ignored { private % }', + 'class Ignored { private "%" = 1 }', + 'class Ignored { private readonly % = 1 }', + 'class Ignored { private static % }', + 'class Ignored { private static readonly % = 1 }', + 'class Ignored { abstract % = 1 }', + 'class Ignored { declare % }', + 'class Ignored { #% }', + 'class Ignored { static #% }', + ], + options: { + selector: 'classProperty', + }, + }, + { + code: ['const ignored = { % };', 'const ignored = { "%": 1 };'], + options: { + selector: 'objectLiteralProperty', + }, + }, + { + code: [ + 'interface Ignored { % }', + 'interface Ignored { "%": string }', + 'type Ignored = { % }', + 'type Ignored = { "%": string }', + ], + options: { + selector: 'typeProperty', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/typeAlias.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/typeAlias.test.ts new file mode 100644 index 000000000000..c4a179ff97b6 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/typeAlias.test.ts @@ -0,0 +1,10 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: ['type % = {};', 'type % = 1;'], + options: { + selector: 'typeAlias', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/typeParameter.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/typeParameter.test.ts new file mode 100644 index 000000000000..b05d1884bfa3 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/typeParameter.test.ts @@ -0,0 +1,15 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'class Ignored<%> {}', + 'function ignored<%>() {}', + 'type Ignored<%> = { ignored: % };', + 'interface Ignored<%> extends Ignored {}', + ], + options: { + selector: 'typeParameter', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention/cases/variable.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/cases/variable.test.ts new file mode 100644 index 000000000000..269c04d813e5 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/naming-convention/cases/variable.test.ts @@ -0,0 +1,20 @@ +import { createTestCases } from './createTestCases'; + +createTestCases([ + { + code: [ + 'const % = 1;', + 'let % = 1;', + 'var % = 1;', + 'const {%} = {ignored: 1};', + 'const {% = 2} = {ignored: 1};', + 'const {...%} = {ignored: 1};', + 'const [%] = [1];', + 'const [% = 1] = [1];', + 'const [...%] = [1];', + ], + options: { + selector: 'variable', + }, + }, +]); diff --git a/packages/eslint-plugin/tests/rules/naming-convention.test.ts b/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts similarity index 66% rename from packages/eslint-plugin/tests/rules/naming-convention.test.ts rename to packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts index 656dade01d73..4c496b6c127b 100644 --- a/packages/eslint-plugin/tests/rules/naming-convention.test.ts +++ b/packages/eslint-plugin/tests/rules/naming-convention/naming-convention.test.ts @@ -1,12 +1,6 @@ /* eslint-disable @typescript-eslint/internal/prefer-ast-types-enum */ -import { TSESLint } from '@typescript-eslint/utils'; -import rule, { MessageIds, Options } from '../../src/rules/naming-convention'; -import { - PredefinedFormatsString, - Selector, - selectorTypeToMessageString, -} from '../../src/rules/naming-convention-utils'; -import { getFixturesRootDir, noFormat, RuleTester } from '../RuleTester'; +import rule from '../../../src/rules/naming-convention'; +import { getFixturesRootDir, noFormat, RuleTester } from '../../RuleTester'; const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', @@ -19,717 +13,8 @@ const parserOptions = { project: './tsconfig.json', }; -const formatTestNames: Readonly< - Record> -> = { - camelCase: { - valid: ['strictCamelCase', 'lower', 'camelCaseUNSTRICT'], - invalid: ['snake_case', 'UPPER_CASE', 'UPPER', 'StrictPascalCase'], - }, - strictCamelCase: { - valid: ['strictCamelCase', 'lower'], - invalid: [ - 'snake_case', - 'UPPER_CASE', - 'UPPER', - 'StrictPascalCase', - 'camelCaseUNSTRICT', - ], - }, - PascalCase: { - valid: [ - 'StrictPascalCase', - 'Pascal', - 'I18n', - 'PascalCaseUNSTRICT', - 'UPPER', - ], - invalid: ['snake_case', 'UPPER_CASE', 'strictCamelCase'], - }, - StrictPascalCase: { - valid: ['StrictPascalCase', 'Pascal', 'I18n'], - invalid: [ - 'snake_case', - 'UPPER_CASE', - 'UPPER', - 'strictCamelCase', - 'PascalCaseUNSTRICT', - ], - }, - UPPER_CASE: { - valid: ['UPPER_CASE', 'UPPER'], - invalid: [ - 'lower', - 'snake_case', - 'SNAKE_case_UNSTRICT', - 'strictCamelCase', - 'StrictPascalCase', - ], - }, - snake_case: { - valid: ['snake_case', 'lower'], - invalid: [ - 'UPPER_CASE', - 'SNAKE_case_UNSTRICT', - 'strictCamelCase', - 'StrictPascalCase', - ], - }, -}; - -const REPLACE_REGEX = /%/g; -// filter to not match `[iI]gnored` -const IGNORED_FILTER = { - match: false, - regex: /.gnored/.source, -}; - -type Cases = { - code: string[]; - options: Omit; -}[]; -function createValidTestCases(cases: Cases): TSESLint.ValidTestCase[] { - const newCases: TSESLint.ValidTestCase[] = []; - - for (const test of cases) { - for (const [formatLoose, names] of Object.entries(formatTestNames)) { - const format = [formatLoose as PredefinedFormatsString]; - for (const name of names.valid) { - const createCase = ( - preparedName: string, - options: Selector, - ): TSESLint.ValidTestCase => ({ - options: [ - { - ...options, - filter: IGNORED_FILTER, - }, - ], - code: `// ${JSON.stringify(options)}\n${test.code - .map(code => code.replace(REPLACE_REGEX, preparedName)) - .join('\n')}`, - }); - - newCases.push( - createCase(name, { - ...test.options, - format, - }), - - // leadingUnderscore - createCase(name, { - ...test.options, - format, - leadingUnderscore: 'forbid', - }), - createCase(`_${name}`, { - ...test.options, - format, - leadingUnderscore: 'require', - }), - createCase(`__${name}`, { - ...test.options, - format, - leadingUnderscore: 'requireDouble', - }), - createCase(`_${name}`, { - ...test.options, - format, - leadingUnderscore: 'allow', - }), - createCase(name, { - ...test.options, - format, - leadingUnderscore: 'allow', - }), - createCase(`__${name}`, { - ...test.options, - format, - leadingUnderscore: 'allowDouble', - }), - createCase(name, { - ...test.options, - format, - leadingUnderscore: 'allowDouble', - }), - createCase(`_${name}`, { - ...test.options, - format, - leadingUnderscore: 'allowSingleOrDouble', - }), - createCase(name, { - ...test.options, - format, - leadingUnderscore: 'allowSingleOrDouble', - }), - createCase(`__${name}`, { - ...test.options, - format, - leadingUnderscore: 'allowSingleOrDouble', - }), - createCase(name, { - ...test.options, - format, - leadingUnderscore: 'allowSingleOrDouble', - }), - - // trailingUnderscore - createCase(name, { - ...test.options, - format, - trailingUnderscore: 'forbid', - }), - createCase(`${name}_`, { - ...test.options, - format, - trailingUnderscore: 'require', - }), - createCase(`${name}__`, { - ...test.options, - format, - trailingUnderscore: 'requireDouble', - }), - createCase(`${name}_`, { - ...test.options, - format, - trailingUnderscore: 'allow', - }), - createCase(name, { - ...test.options, - format, - trailingUnderscore: 'allow', - }), - createCase(`${name}__`, { - ...test.options, - format, - trailingUnderscore: 'allowDouble', - }), - createCase(name, { - ...test.options, - format, - trailingUnderscore: 'allowDouble', - }), - createCase(`${name}_`, { - ...test.options, - format, - trailingUnderscore: 'allowSingleOrDouble', - }), - createCase(name, { - ...test.options, - format, - trailingUnderscore: 'allowSingleOrDouble', - }), - createCase(`${name}__`, { - ...test.options, - format, - trailingUnderscore: 'allowSingleOrDouble', - }), - createCase(name, { - ...test.options, - format, - trailingUnderscore: 'allowSingleOrDouble', - }), - - // prefix - createCase(`MyPrefix${name}`, { - ...test.options, - format, - prefix: ['MyPrefix'], - }), - createCase(`MyPrefix2${name}`, { - ...test.options, - format, - prefix: ['MyPrefix1', 'MyPrefix2'], - }), - - // suffix - createCase(`${name}MySuffix`, { - ...test.options, - format, - suffix: ['MySuffix'], - }), - createCase(`${name}MySuffix2`, { - ...test.options, - format, - suffix: ['MySuffix1', 'MySuffix2'], - }), - ); - } - } - } - - return newCases; -} -function createInvalidTestCases( - cases: Cases, -): TSESLint.InvalidTestCase[] { - const newCases: TSESLint.InvalidTestCase[] = []; - - for (const test of cases) { - for (const [formatLoose, names] of Object.entries(formatTestNames)) { - const format = [formatLoose as PredefinedFormatsString]; - for (const name of names.invalid) { - const createCase = ( - preparedName: string, - options: Selector, - messageId: MessageIds, - data: Record = {}, - ): TSESLint.InvalidTestCase => { - const selectors = Array.isArray(test.options.selector) - ? test.options.selector - : [test.options.selector]; - const errorsTemplate = selectors.map(selector => ({ - messageId, - ...(selector !== 'default' && - selector !== 'variableLike' && - selector !== 'memberLike' && - selector !== 'typeLike' && - selector !== 'property' && - selector !== 'method' - ? { - data: { - type: selectorTypeToMessageString(selector), - name: preparedName, - ...data, - }, - } - : // meta-types will use the correct selector, so don't assert on data shape - {}), - })); - - const errors: { - data?: { type: string; name: string }; - messageId: MessageIds; - }[] = []; - test.code.forEach(() => errors.push(...errorsTemplate)); - - return { - options: [ - { - ...options, - filter: IGNORED_FILTER, - }, - ], - code: `// ${JSON.stringify(options)}\n${test.code - .map(code => code.replace(REPLACE_REGEX, preparedName)) - .join('\n')}`, - errors: errors, - }; - }; - - const prefixSingle = ['MyPrefix']; - const prefixMulti = ['MyPrefix1', 'MyPrefix2']; - const suffixSingle = ['MySuffix']; - const suffixMulti = ['MySuffix1', 'MySuffix2']; - - newCases.push( - createCase( - name, - { - ...test.options, - format, - }, - 'doesNotMatchFormat', - { formats: format.join(', ') }, - ), - - // leadingUnderscore - createCase( - `_${name}`, - { - ...test.options, - format, - leadingUnderscore: 'forbid', - }, - 'unexpectedUnderscore', - { position: 'leading' }, - ), - createCase( - name, - { - ...test.options, - format, - leadingUnderscore: 'require', - }, - 'missingUnderscore', - { position: 'leading', count: 'one' }, - ), - createCase( - name, - { - ...test.options, - format, - leadingUnderscore: 'requireDouble', - }, - 'missingUnderscore', - { position: 'leading', count: 'two' }, - ), - createCase( - `_${name}`, - { - ...test.options, - format, - leadingUnderscore: 'requireDouble', - }, - 'missingUnderscore', - { position: 'leading', count: 'two' }, - ), - - // trailingUnderscore - createCase( - `${name}_`, - { - ...test.options, - format, - trailingUnderscore: 'forbid', - }, - 'unexpectedUnderscore', - { position: 'trailing' }, - ), - createCase( - name, - { - ...test.options, - format, - trailingUnderscore: 'require', - }, - 'missingUnderscore', - { position: 'trailing', count: 'one' }, - ), - createCase( - name, - { - ...test.options, - format, - trailingUnderscore: 'requireDouble', - }, - 'missingUnderscore', - { position: 'trailing', count: 'two' }, - ), - createCase( - `${name}_`, - { - ...test.options, - format, - trailingUnderscore: 'requireDouble', - }, - 'missingUnderscore', - { position: 'trailing', count: 'two' }, - ), - - // prefix - createCase( - name, - { - ...test.options, - format, - prefix: prefixSingle, - }, - 'missingAffix', - { position: 'prefix', affixes: prefixSingle.join(', ') }, - ), - createCase( - name, - { - ...test.options, - format, - prefix: prefixMulti, - }, - 'missingAffix', - { - position: 'prefix', - affixes: prefixMulti.join(', '), - }, - ), - - // suffix - createCase( - name, - { - ...test.options, - format, - suffix: suffixSingle, - }, - 'missingAffix', - { position: 'suffix', affixes: suffixSingle.join(', ') }, - ), - createCase( - name, - { - ...test.options, - format, - suffix: suffixMulti, - }, - 'missingAffix', - { - position: 'suffix', - affixes: suffixMulti.join(', '), - }, - ), - ); - } - } - } - - return newCases; -} - -const cases: Cases = [ - // #region default - { - code: [ - 'const % = 1;', - 'function % () {}', - '(function (%) {});', - 'class Ignored { constructor(private %) {} }', - 'const ignored = { % };', - 'interface Ignored { %: string }', - 'type Ignored = { %: string }', - 'class Ignored { private % = 1 }', - 'class Ignored { #% = 1 }', - 'class Ignored { constructor(private %) {} }', - 'class Ignored { #%() {} }', - 'class Ignored { private %() {} }', - 'const ignored = { %() {} };', - 'class Ignored { private get %() {} }', - 'enum Ignored { % }', - 'abstract class % {}', - 'interface % { }', - 'type % = { };', - 'enum % {}', - 'interface Ignored<%> extends Ignored {}', - ], - options: { - selector: 'default', - filter: '[iI]gnored', - }, - }, - // #endregion default - - // #region variable - { - code: [ - 'const % = 1;', - 'let % = 1;', - 'var % = 1;', - 'const {%} = {ignored: 1};', - 'const {% = 2} = {ignored: 1};', - 'const {...%} = {ignored: 1};', - 'const [%] = [1];', - 'const [% = 1] = [1];', - 'const [...%] = [1];', - ], - options: { - selector: 'variable', - }, - }, - // #endregion variable - - // #region function - { - code: ['function % () {}', '(function % () {});', 'declare function % ();'], - options: { - selector: 'function', - }, - }, - // #endregion function - - // #region parameter - { - code: [ - 'function ignored(%) {}', - '(function (%) {});', - 'declare function ignored(%);', - 'function ignored({%}) {}', - 'function ignored(...%) {}', - 'function ignored({% = 1}) {}', - 'function ignored({...%}) {}', - 'function ignored([%]) {}', - 'function ignored([% = 1]) {}', - 'function ignored([...%]) {}', - ], - options: { - selector: 'parameter', - }, - }, - // #endregion parameter - - // #region property - { - code: [ - 'class Ignored { private % }', - 'class Ignored { private "%" = 1 }', - 'class Ignored { private readonly % = 1 }', - 'class Ignored { private static % }', - 'class Ignored { private static readonly % = 1 }', - 'class Ignored { abstract % = 1 }', - 'class Ignored { declare % }', - 'class Ignored { #% }', - 'class Ignored { static #% }', - ], - options: { - selector: 'classProperty', - }, - }, - { - code: ['const ignored = { % };', 'const ignored = { "%": 1 };'], - options: { - selector: 'objectLiteralProperty', - }, - }, - { - code: [ - 'interface Ignored { % }', - 'interface Ignored { "%": string }', - 'type Ignored = { % }', - 'type Ignored = { "%": string }', - ], - options: { - selector: 'typeProperty', - }, - }, - // #endregion property - - // #region parameterProperty - { - code: [ - 'class Ignored { constructor(private %) {} }', - 'class Ignored { constructor(readonly %) {} }', - 'class Ignored { constructor(private readonly %) {} }', - ], - options: { - selector: 'parameterProperty', - }, - }, - { - code: ['class Ignored { constructor(private readonly %) {} }'], - options: { - selector: 'parameterProperty', - modifiers: ['readonly'], - }, - }, - // #endregion parameterProperty - - // #region method - { - code: [ - 'class Ignored { private %() {} }', - 'class Ignored { private "%"() {} }', - 'class Ignored { private readonly %() {} }', - 'class Ignored { private static %() {} }', - 'class Ignored { private static readonly %() {} }', - 'class Ignored { private % = () => {} }', - 'class Ignored { abstract %() }', - 'class Ignored { declare %() }', - 'class Ignored { #%() }', - 'class Ignored { static #%() }', - ], - options: { - selector: 'classMethod', - }, - }, - { - code: [ - 'const ignored = { %() {} };', - 'const ignored = { "%"() {} };', - 'const ignored = { %: () => {} };', - ], - options: { - selector: 'objectLiteralMethod', - }, - }, - { - code: [ - 'interface Ignored { %(): string }', - 'interface Ignored { "%"(): string }', - 'type Ignored = { %(): string }', - 'type Ignored = { "%"(): string }', - ], - options: { - selector: 'typeMethod', - }, - }, - // #endregion method - - // #region accessor - { - code: [ - 'const ignored = { get %() {} };', - 'const ignored = { set "%"(ignored) {} };', - 'class Ignored { private get %() {} }', - 'class Ignored { private set "%"(ignored) {} }', - 'class Ignored { private static get %() {} }', - 'class Ignored { static get #%() {} }', - ], - options: { - selector: 'accessor', - }, - }, - // #endregion accessor - - // #region enumMember - { - code: ['enum Ignored { % }', 'enum Ignored { "%" }'], - options: { - selector: 'enumMember', - }, - }, - // #endregion enumMember - - // #region class - { - code: ['class % {}', 'abstract class % {}', 'const ignored = class % {}'], - options: { - selector: 'class', - }, - }, - // #endregion class - - // #region interface - { - code: ['interface % {}'], - options: { - selector: 'interface', - }, - }, - // #endregion interface - - // #region typeAlias - { - code: ['type % = {};', 'type % = 1;'], - options: { - selector: 'typeAlias', - }, - }, - // #endregion typeAlias - - // #region enum - { - code: ['enum % {}'], - options: { - selector: 'enum', - }, - }, - // #endregion enum - - // #region typeParameter - { - code: [ - 'class Ignored<%> {}', - 'function ignored<%>() {}', - 'type Ignored<%> = { ignored: % };', - 'interface Ignored<%> extends Ignored {}', - ], - options: { - selector: 'typeParameter', - }, - }, - // #endregion typeParameter -]; - ruleTester.run('naming-convention', rule, { valid: [ - ...createValidTestCases(cases), { code: ` const child_process = require('child_process'); @@ -1496,7 +781,6 @@ ruleTester.run('naming-convention', rule, { options: [], errors: [{ messageId: 'doesNotMatchFormat' }], }, - ...createInvalidTestCases(cases), { code: ` const child_process = require('child_process');