diff --git a/README.md b/README.md index 5f15127a..7c66ad2b 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,7 @@ export default [ | [no-identical-tests](docs/rules/no-identical-tests.md) | disallow identical tests | ✅ | 🔧 | | | | [no-only-tests](docs/rules/no-only-tests.md) | disallow the test case property `only` | ✅ | | 💡 | | | [prefer-output-null](docs/rules/prefer-output-null.md) | disallow invalid RuleTester test cases where the `output` matches the `code` | ✅ | 🔧 | | | +| [require-test-case-name](docs/rules/require-test-case-name.md) | require test cases to have a `name` property under certain conditions | | | | | | [test-case-property-ordering](docs/rules/test-case-property-ordering.md) | require the properties of a test case to be placed in a consistent order | | 🔧 | | | | [test-case-shorthand-strings](docs/rules/test-case-shorthand-strings.md) | enforce consistent usage of shorthand strings for test cases with no options | | 🔧 | | | diff --git a/docs/rules/require-test-case-name.md b/docs/rules/require-test-case-name.md new file mode 100644 index 00000000..03b69e65 --- /dev/null +++ b/docs/rules/require-test-case-name.md @@ -0,0 +1,63 @@ +# Require test cases to have a `name` property under certain conditions (`eslint-plugin/require-test-case-name`) + + + +This rule enforces that test cases include a `name` property, under certain circumstances based on the configuration. + +## Rule Details + +This rule aims to ensure test suites are producing logs in a form that make it easy to identify failing test, when they happen. +For thoroughly tested rules, it's not uncommon to have the same `code` across multiple test cases, with only `options` or `settings` differing between them. +Requiring these test cases to have a `name` helps ensure the test output is meaningful and distinct. + +### Options + +This rule has one option. + +#### `require: 'always' | 'objects' | 'objects-with-config'` + +- `always`: all test cases should have a `name` property (this means that no shorthand string test cases are allowed as a side effect) +- `objects`: requires that a `name` property is present in all `object`-based test cases. +- `objects-with-config` (default): requires that test cases that have `options` or `settings` defined, should also have a `name` property. + +Examples of **incorrect** code for this rule: + +```js +// invalid; require: objects-with-config (default) +const testCase1 = { + code: 'foo', + options: ['baz'], +}; + +// invalid; require: objects +const testCase2 = { + code: 'foo', +}; + +// invalid; require: always +const testCase3 = 'foo'; +``` + +Examples of **correct** code for this rule: + +```js +// require: objects-with-config, objects +const testCase1 = 'foo'; + +// require: objects-with-config, objects, always +const testCase2 = { + code: 'foo', + options: ['baz'], + name: "foo (option: ['baz'])", +}; + +// require: objects-with-config, objects, always +const testCase4 = { + code: 'foo', + name: 'foo without options', +}; +``` + +## When Not to Use It + +If you aren't concerned with the nature of the test logs or don't want to require `name` on test cases. diff --git a/lib/index.ts b/lib/index.ts index 4d99210f..1b13e8cc 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -36,6 +36,7 @@ import requireMetaHasSuggestions from './rules/require-meta-has-suggestions.ts'; import requireMetaSchemaDescription from './rules/require-meta-schema-description.ts'; import requireMetaSchema from './rules/require-meta-schema.ts'; import requireMetaType from './rules/require-meta-type.ts'; +import requireTestCaseName from './rules/require-test-case-name.ts'; import testCasePropertyOrdering from './rules/test-case-property-ordering.ts'; import testCaseShorthandStrings from './rules/test-case-shorthand-strings.ts'; @@ -115,6 +116,7 @@ const allRules = { 'require-meta-schema-description': requireMetaSchemaDescription, 'require-meta-schema': requireMetaSchema, 'require-meta-type': requireMetaType, + 'require-test-case-name': requireTestCaseName, 'test-case-property-ordering': testCasePropertyOrdering, 'test-case-shorthand-strings': testCaseShorthandStrings, } satisfies Record; diff --git a/lib/rules/require-test-case-name.ts b/lib/rules/require-test-case-name.ts new file mode 100644 index 00000000..cf74bec3 --- /dev/null +++ b/lib/rules/require-test-case-name.ts @@ -0,0 +1,141 @@ +import type { Rule } from 'eslint'; + +import { evaluateObjectProperties, getKeyName, getTestInfo } from '../utils.ts'; +import type { TestInfo } from '../types.ts'; + +type TestCaseData = { + node: NonNullable; + isObject: boolean; + hasName: boolean; + hasConfig: boolean; +}; + +const violationFilters = { + always: (testCase: TestCaseData) => !testCase.hasName, + objects: (testCase: TestCaseData) => testCase.isObject && !testCase.hasName, + 'objects-with-config': (testCase: TestCaseData) => + testCase.isObject && testCase.hasConfig && !testCase.hasName, +} satisfies Record boolean>; + +const violationMessages = { + always: 'nameRequiredAlways', + objects: 'nameRequiredObjects', + 'objects-with-config': 'nameRequiredObjectsWithConfig', +} satisfies Record; + +type Options = { + require: 'always' | 'objects' | 'objects-with-config'; +}; + +const rule: Rule.RuleModule = { + meta: { + type: 'suggestion', + docs: { + description: + 'require test cases to have a `name` property under certain conditions', + category: 'Tests', + recommended: false, + url: 'https://github.com/eslint-community/eslint-plugin-eslint-plugin/tree/HEAD/docs/rules/require-test-case-name.md', + }, + schema: [ + { + additionalProperties: false, + properties: { + require: { + description: + 'When should the name property be required on a test case object.', + enum: ['always', 'objects', 'objects-with-config'], + }, + }, + type: 'object', + }, + ], + defaultOptions: [{ require: 'objects-with-config' }], + messages: { + nameRequiredAlways: + 'This test case is missing the `name` property. All test cases should have a name property.', + nameRequiredObjects: + 'This test case is missing the `name` property. Test cases defined as objects should have a name property.', + nameRequiredObjectsWithConfig: + 'This test case is missing the `name` property. Test cases defined as objects with additional configuration should have a name property.', + }, + }, + + create(context) { + const { require: requireOption = 'objects-with-config' }: Options = + context.options[0] || {}; + const sourceCode = context.sourceCode; + + /** + * Validates test cases and reports them if found in violation + * @param cases A list of test case nodes + */ + function validateTestCases(cases: TestInfo['valid']): void { + // Gather all of the information from each test case + const testCaseData: TestCaseData[] = cases + .filter((testCase) => !!testCase) + .map((testCase) => { + if ( + testCase.type === 'Literal' || + testCase.type === 'TemplateLiteral' + ) { + return { + node: testCase, + isObject: false, + hasName: false, + hasConfig: false, + }; + } + if (testCase.type === 'ObjectExpression') { + let hasName = false; + let hasConfig = false; + + // evaluateObjectProperties is used here to expand spread elements + for (const property of evaluateObjectProperties( + testCase, + sourceCode.scopeManager, + )) { + if (property.type === 'Property') { + const keyName = getKeyName( + property, + sourceCode.getScope(testCase), + ); + if (keyName === 'name') { + hasName = true; + } else if (keyName === 'options' || keyName === 'settings') { + hasConfig = true; + } + } + } + + return { + node: testCase, + isObject: true, + hasName, + hasConfig, + }; + } + return null; + }) + .filter((testCase) => !!testCase); + + const violations = testCaseData.filter(violationFilters[requireOption]); + for (const violation of violations) { + context.report({ + node: violation.node, + messageId: violationMessages[requireOption], + }); + } + } + + return { + Program(ast) { + getTestInfo(context, ast) + .map((testRun) => [...testRun.valid, ...testRun.invalid]) + .forEach(validateTestCases); + }, + }; + }, +}; + +export default rule; diff --git a/tests/lib/rules/consistent-output.ts b/tests/lib/rules/consistent-output.ts index 8daa2a4b..6fc92040 100644 --- a/tests/lib/rules/consistent-output.ts +++ b/tests/lib/rules/consistent-output.ts @@ -67,6 +67,7 @@ ruleTester.run('consistent-output', rule, { }); `, options: ['always'], + name: 'test case with code, output, and errors (options: always)', }, ` new NotRuleTester().run('foo', bar, { @@ -118,6 +119,7 @@ ruleTester.run('consistent-output', rule, { `, options: ['always'], errors: [ERROR], + name: 'invalid test case missing output (options: always)', }, ], }); diff --git a/tests/lib/rules/meta-property-ordering.ts b/tests/lib/rules/meta-property-ordering.ts index e2cfd024..5ac324ff 100644 --- a/tests/lib/rules/meta-property-ordering.ts +++ b/tests/lib/rules/meta-property-ordering.ts @@ -65,6 +65,7 @@ ruleTester.run('test-case-property-ordering', rule, { create() {}, };`, options: [['schema', 'docs']], + name: 'custom order (options: [schema, docs])', }, ` module.exports = { @@ -179,6 +180,7 @@ ruleTester.run('test-case-property-ordering', rule, { data: { order: ['type', 'docs', 'fixable'].join(', ') }, }, ], + name: 'custom order with extra prop (options: [type, docs, fixable])', }, ], }); diff --git a/tests/lib/rules/no-property-in-node.ts b/tests/lib/rules/no-property-in-node.ts index 57106ee1..c98843e1 100644 --- a/tests/lib/rules/no-property-in-node.ts +++ b/tests/lib/rules/no-property-in-node.ts @@ -110,6 +110,7 @@ ruleTester.run('no-property-in-node', rule, { additionalNodeTypeFiles: [/not-found/], }, ], + name: 'additionalNodeTypeFiles with no matches', }, ], invalid: [ @@ -204,6 +205,7 @@ ruleTester.run('no-property-in-node', rule, { messageId: 'in', }, ], + name: 'additionalNodeTypeFiles with matches', }, ], }); diff --git a/tests/lib/rules/report-message-format.ts b/tests/lib/rules/report-message-format.ts index 37331322..c024ef8b 100644 --- a/tests/lib/rules/report-message-format.ts +++ b/tests/lib/rules/report-message-format.ts @@ -30,6 +30,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['foo'], + name: 'with no languageOptions (options: foo)', }, { // ESM @@ -42,6 +43,7 @@ ruleTester.run('report-message-format', rule, { `, options: ['foo'], languageOptions: { sourceType: 'module' }, + name: 'ESM (options: foo)', }, { // With message as variable. @@ -54,6 +56,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['foo'], + name: 'with message as variable (options: foo)', }, { // With message as variable but cannot statically determine its type. @@ -66,6 +69,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['foo'], + name: 'with message as variable but cannot statically determine its type (options: foo)', }, { code: ` @@ -76,6 +80,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['f'], + name: 'with no languageOptions (options: f)', }, { code: ` @@ -86,6 +91,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['foo'], + name: 'with message as variable but variable is not defined in scope (options: foo)', }, { code: ` @@ -96,6 +102,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['^foo$'], + name: 'no match with regex (options: ^foo$)', }, { code: ` @@ -106,6 +113,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['^foo$'], + name: 'object param with regex (options: ^foo$)', }, { // Suggestion function @@ -117,6 +125,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['^foo$'], + name: 'suggestion function (options: ^foo$)', }, { // Suggestion message @@ -128,6 +137,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['^foo$'], + name: 'suggestion message (options: ^foo$)', }, { // Suggestion message with ternary expression @@ -139,6 +149,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['^foo$'], + name: 'suggestion message with ternary (options: ^foo$)', }, { code: ` @@ -149,6 +160,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['bar'], + name: 'substring (options: bar)', }, { code: ` @@ -159,6 +171,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['bar'], + name: 'substring with backticks (options: bar)', }, { code: ` @@ -169,6 +182,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['bar'], + name: 'empty report (options: bar)', }, { code: ` @@ -179,6 +193,7 @@ ruleTester.run('report-message-format', rule, { }; `, options: ['foo'], + name: 'empty report (options: foo)', }, { code: ` @@ -193,6 +208,7 @@ ruleTester.run('report-message-format', rule, { } `, options: ['foo'], + name: 'multiple meta.messages (options: foo)', }, 'module.exports = {};', // No rule. ], diff --git a/tests/lib/rules/require-meta-docs-description.ts b/tests/lib/rules/require-meta-docs-description.ts index 10f37b3e..1db2c5ba 100644 --- a/tests/lib/rules/require-meta-docs-description.ts +++ b/tests/lib/rules/require-meta-docs-description.ts @@ -91,6 +91,7 @@ ruleTester.run('require-meta-docs-description', rule, { }; `, options: [{ pattern: '^myPrefix' }], + name: "custom pattern (pattern: '^myPrefix')", }, { code: ` @@ -100,6 +101,7 @@ ruleTester.run('require-meta-docs-description', rule, { }; `, options: [{ pattern: '.+' }], // any description allowed + name: "custom pattern (pattern: '.+')", }, // `meta` in variable, `description` present. ` @@ -290,6 +292,7 @@ ruleTester.run('require-meta-docs-description', rule, { type: 'Literal', }, ], + name: "custom pattern (pattern: '^myPrefix')", }, ], }); diff --git a/tests/lib/rules/require-meta-docs-recommended.ts b/tests/lib/rules/require-meta-docs-recommended.ts index 8d709e58..334a4725 100644 --- a/tests/lib/rules/require-meta-docs-recommended.ts +++ b/tests/lib/rules/require-meta-docs-recommended.ts @@ -62,6 +62,7 @@ ruleTester.run('require-meta-docs-recommended', rule, { }; `, options: [{ allowNonBoolean: true }], + name: 'undefined recommended (allowNonBoolean: true)', }, { code: ` @@ -71,6 +72,7 @@ ruleTester.run('require-meta-docs-recommended', rule, { }; `, options: [{ allowNonBoolean: true }], + name: 'string recommended (allowNonBoolean: true)', }, { code: ` @@ -82,6 +84,7 @@ ruleTester.run('require-meta-docs-recommended', rule, { }; `, options: [{ allowNonBoolean: true }], + name: 'multiple spreads with string recommended (allowNonBoolean: true)', }, ], @@ -262,6 +265,7 @@ ruleTester.run('require-meta-docs-recommended', rule, { errors: [ { messageId: 'missing', type: 'FunctionExpression', suggestions: [] }, ], + name: 'missing recommended (allowNonBoolean: true)', }, ], }); diff --git a/tests/lib/rules/require-meta-docs-url.ts b/tests/lib/rules/require-meta-docs-url.ts index e4e9b31c..a8406059 100644 --- a/tests/lib/rules/require-meta-docs-url.ts +++ b/tests/lib/rules/require-meta-docs-url.ts @@ -54,6 +54,7 @@ tester.run('require-meta-docs-url', rule, { pattern: 'path/to/{{name}}.md', }, ], + name: "no filename (pattern: 'path/to/{{name}}.md')", }, { filename: 'test-rule', @@ -68,6 +69,7 @@ tester.run('require-meta-docs-url', rule, { pattern: 'path/to/{{name}}.md', }, ], + name: "matching path (pattern: 'path/to/{{name}}.md')", }, { // CJS file extension @@ -79,6 +81,7 @@ tester.run('require-meta-docs-url', rule, { } `, options: [{ pattern: 'path/to/{{name}}.md' }], + name: "matching path with cjs extension (pattern: 'path/to/{{name}}.md')", }, { // ESM @@ -95,6 +98,7 @@ tester.run('require-meta-docs-url', rule, { }, ], languageOptions: { sourceType: 'module' }, + name: "ESM (pattern: 'path/to/{{name}}.md')", }, { // TypeScript @@ -107,6 +111,7 @@ tester.run('require-meta-docs-url', rule, { `, options: [{ pattern: 'path/to/{{name}}.md' }], languageOptions: { sourceType: 'module' }, + name: "TypeScript (pattern: 'path/to/{{name}}.md')", }, { // `url` in variable. @@ -123,6 +128,7 @@ tester.run('require-meta-docs-url', rule, { pattern: 'path/to/{{name}}.md', }, ], + name: "url in variable (pattern: 'path/to/{{name}}.md')", }, { // Can't determine `url` value statically. @@ -138,6 +144,7 @@ tester.run('require-meta-docs-url', rule, { pattern: 'path/to/{{name}}.md', }, ], + name: "undefined variable reference (pattern: 'path/to/{{name}}.md')", }, { // Can't determine `url` value statically. @@ -153,6 +160,7 @@ tester.run('require-meta-docs-url', rule, { pattern: 'path/to/{{name}}.md', }, ], + name: "undefined function (pattern: 'path/to/{{name}}.md')", }, { // Spread. @@ -166,6 +174,7 @@ tester.run('require-meta-docs-url', rule, { } `, options: [{ pattern: 'path/to/{{name}}.md' }], + name: "spread (pattern: 'path/to/{{name}}.md')", }, ], @@ -372,6 +381,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'FunctionExpression' }], + name: "missing meta (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -387,6 +397,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'Identifier' }], + name: "meta as variable (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -402,6 +413,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'Literal' }], + name: "meta set to number (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -417,6 +429,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "empty meta (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -434,6 +447,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing docs with other prop having no trailing comma (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -451,6 +465,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing docs (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -468,6 +483,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'Identifier' }], + name: "docs as variable (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -485,6 +501,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "docs as empty object (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -504,6 +521,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing url with other prop as string (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -523,6 +541,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing url with other prop as string with trailing comma (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -542,6 +561,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'wrongType', type: 'Literal' }], + name: "url as number (pattern: 'plugin-name/{{ name }}.md')", }, { code: ` @@ -561,6 +581,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "spread url variable (pattern: 'plugin-name/{{ name }}.md')", }, // ------------------------------------------------------------------------- @@ -578,6 +599,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'FunctionExpression' }], + name: "missing meta with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -594,6 +616,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'Identifier' }], + name: "meta as variable with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -610,6 +633,7 @@ tester.run('require-meta-docs-url', rule, { }, ], errors: [{ messageId: 'missing', type: 'Literal' }], + name: "meta set to number with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -635,6 +659,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "empty meta with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // URL missing, spreads present. @@ -658,6 +683,7 @@ url: "plugin-name/test.md" } }; `, options: [{ pattern: 'plugin-name/{{ name }}.md' }], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing url inside spreads (pattern: 'plugin-name/{{ name }}.md')", }, { // URL wrong inside spreads. @@ -680,6 +706,7 @@ url: "plugin-name/test.md" } }; `, options: [{ pattern: 'plugin-name/{{ name }}.md' }], errors: [{ messageId: 'mismatch', type: 'Literal' }], + name: "url wrong inside spreads (pattern: 'plugin-name/{{ name }}.md')", }, { // CJS file extension @@ -706,6 +733,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "empty meta with filename and cjs extension (pattern: 'plugin-name/{{ name }}.md')", }, { // ESM @@ -733,6 +761,7 @@ url: "plugin-name/test.md" ], languageOptions: { sourceType: 'module' }, errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "empty meta with filename and esm (pattern: 'plugin-name/{{ name }}.md')", }, { // TypeScript @@ -756,6 +785,7 @@ url: "plugin-name/test.md" options: [{ pattern: 'plugin-name/{{ name }}.md' }], languageOptions: { sourceType: 'module' }, errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "empty meta with filename and TypeScript (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -784,6 +814,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing docs with other prop having no trailing comma > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -812,6 +843,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing docs > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -830,6 +862,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'Identifier' }], + name: "docs as variable > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -857,6 +890,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "docs as empty object > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -887,6 +921,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing url with other prop as string > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -917,6 +952,7 @@ url: "plugin-name/test.md", }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "missing url with other prop as string with trailing comma > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -951,6 +987,7 @@ url: "plugin-name/test.md", type: 'Literal', }, ], + name: "url as number > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // `url` in variable, can't autofix it. @@ -976,6 +1013,7 @@ url: "plugin-name/test.md", type: 'Identifier', }, ], + name: "url in variable > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // `url` is `null`. @@ -1007,6 +1045,7 @@ url: "plugin-name/test.md", type: 'Literal', }, ], + name: "url is null > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // `url` is `undefined`. @@ -1038,6 +1077,7 @@ url: "plugin-name/test.md", type: 'Identifier', }, ], + name: "url is undefined > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { filename: 'test.js', @@ -1068,6 +1108,7 @@ url: "plugin-name/test.md" }, ], errors: [{ messageId: 'missing', type: 'ObjectExpression' }], + name: "spread url variable > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // Function rule in variable. @@ -1081,6 +1122,7 @@ url: "plugin-name/test.md" type: 'FunctionExpression', }, ], + name: "function rule in variable > with filename (pattern: 'plugin-name/{{ name }}.md')", }, { // Object rule in variable. @@ -1098,6 +1140,7 @@ url: "plugin-name/test.md" type: 'ObjectExpression', }, ], + name: "object rule in variable > with filename (pattern: 'plugin-name/{{ name }}.md')", }, ], }); diff --git a/tests/lib/rules/require-meta-fixable.ts b/tests/lib/rules/require-meta-fixable.ts index f0210ec2..82661dc6 100644 --- a/tests/lib/rules/require-meta-fixable.ts +++ b/tests/lib/rules/require-meta-fixable.ts @@ -165,6 +165,7 @@ ruleTester.run('require-meta-fixable', rule, { }; `, options: [{ catchNoFixerButFixableProperty: false }], + name: 'fixable code (catchNoFixerButFixableProperty: false)', }, { code: ` @@ -174,6 +175,7 @@ ruleTester.run('require-meta-fixable', rule, { }; `, options: [{ catchNoFixerButFixableProperty: false }], + name: 'fixable whitespace (catchNoFixerButFixableProperty: false)', }, // catchNoFixerButFixableProperty = true { @@ -188,6 +190,7 @@ ruleTester.run('require-meta-fixable', rule, { }; `, options: [{ catchNoFixerButFixableProperty: true }], + name: 'fixable code (catchNoFixerButFixableProperty: true)', }, // Spread in meta. ` @@ -211,6 +214,7 @@ ruleTester.run('require-meta-fixable', rule, { }; `, options: [{ catchNoFixerButFixableProperty: true }], + name: 'spread in report (catchNoFixerButFixableProperty: true)', }, // No rule present. `const foo = { fix: [{}]}; context.report({node,message,fix});`, @@ -320,6 +324,7 @@ ruleTester.run('require-meta-fixable', rule, { `, options: [{ catchNoFixerButFixableProperty: true }], errors: [{ messageId: 'noFixerButFixableValue', type: 'Literal' }], + name: 'fixable code (catchNoFixerButFixableProperty: true)', }, { code: ` @@ -330,6 +335,7 @@ ruleTester.run('require-meta-fixable', rule, { `, options: [{ catchNoFixerButFixableProperty: true }], errors: [{ messageId: 'noFixerButFixableValue', type: 'Literal' }], + name: 'fixable whitespace (catchNoFixerButFixableProperty: true)', }, { code: ` @@ -340,6 +346,7 @@ ruleTester.run('require-meta-fixable', rule, { `, options: [{ catchNoFixerButFixableProperty: true }], errors: [{ messageId: 'missing', type: 'Literal' }], + name: 'fixable null (catchNoFixerButFixableProperty: true)', }, ], }); diff --git a/tests/lib/rules/require-meta-schema.ts b/tests/lib/rules/require-meta-schema.ts index 6bc9b900..c66ae7de 100644 --- a/tests/lib/rules/require-meta-schema.ts +++ b/tests/lib/rules/require-meta-schema.ts @@ -140,11 +140,13 @@ ruleTester.run('require-meta-schema', rule, { }; `, options: [{ requireSchemaPropertyWhenOptionless: false }], + name: 'no schema (requireSchemaPropertyWhenOptionless: false)', }, { // requireSchemaPropertyWhenOptionless = false, no `meta`. code: 'module.exports = { create(context) {} };', options: [{ requireSchemaPropertyWhenOptionless: false }], + name: 'no meta (requireSchemaPropertyWhenOptionless: false)', }, // Spread. ` @@ -244,6 +246,7 @@ schema: [] ], }, ], + name: 'no schema (requireSchemaPropertyWhenOptionless: true)', }, { // ESM @@ -323,6 +326,7 @@ schema: [] }, output: null, options: [{ requireSchemaPropertyWhenOptionless: false }], errors: [{ messageId: 'wrongType', type: 'Literal', suggestions: [] }], + name: 'schema is null (requireSchemaPropertyWhenOptionless: false)', }, { code: ` @@ -406,6 +410,7 @@ schema: [] }, errors: [ { messageId: 'foundOptionsUsage', type: 'Property', suggestions: [] }, ], + name: 'empty schema (requireSchemaPropertyWhenOptionless: false)', }, { // No schema, but using rule options, requireSchemaPropertyWhenOptionless = false. @@ -424,6 +429,7 @@ schema: [] }, suggestions: [], }, ], + name: 'no schema with rule options (requireSchemaPropertyWhenOptionless: false)', }, { // No schema, but using rule options, should have no suggestions. diff --git a/tests/lib/rules/require-test-case-name.ts b/tests/lib/rules/require-test-case-name.ts new file mode 100644 index 00000000..b85ba8e7 --- /dev/null +++ b/tests/lib/rules/require-test-case-name.ts @@ -0,0 +1,452 @@ +import { RuleTester } from 'eslint'; + +import rule from '../../../lib/rules/require-test-case-name.ts'; + +/** + * Returns the code for some valid test cases + * @param cases The code representation of valid test cases + * @returns Code representing the test cases + */ +function getTestCases(valid: string[], invalid: string[] = []): string { + return ` + new RuleTester().run('foo', bar, { + valid: [ + ${valid.join(',\n ')}, + ], + invalid: [ + ${invalid.join(',\n ')}, + ] + }); + `; +} + +const alwaysError = { + messageId: 'nameRequiredAlways', +}; + +const objectsError = { + messageId: 'nameRequiredObjects', +}; + +const objectsWithConfigError = { + messageId: 'nameRequiredObjectsWithConfig', +}; + +const ruleTester = new RuleTester({ + languageOptions: { sourceType: 'commonjs' }, +}); +ruleTester.run('test-case-name-property', rule, { + valid: [ + // default (object-with-config) + getTestCases(['"foo"']), + getTestCases(['"foo"', '"bar"']), + getTestCases(['"foo"', '"bar"', '{ code: "foo" }']), + getTestCases(['{ code: "foo" }', '{ code: "bar", name: "my test" }']), + getTestCases([ + '{ code: "foo" }', + '{ code: "foo", name: "my test", options: ["bar"] }', + '{ code: "bar", name: "my other test" }', + ]), + getTestCases([ + '{ code: "foo" }', + '{ code: "foo", name: "my test", settings: { setting1: "bar" } }', + '{ code: "bar", name: "my other test" }', + ]), + getTestCases([ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" } }', + ]), + getTestCases(['foo']), + getTestCases(['{ code: foo }']), + getTestCases(['foo()']), + getTestCases(['{ code: foo() }']), + // eslint-disable-next-line no-template-curly-in-string -- testing edge cases + getTestCases(['`foo;${bar}`']), + getTestCases([], ['{ code: "foo", errors: ["some error"] }']), + getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "bar", name: "my test", errors: ["some error"] }', + ], + ), + getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "foo", name: "my test", options: ["bar"], errors: ["some error"] }', + '{ code: "bar", name: "my other test", errors: ["some error"] }', + ], + ), + getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "foo", name: "my test", settings: { setting1: "bar" } }', + '{ code: "bar", name: "my other test", errors: ["some error"] }', + ], + ), + getTestCases( + [], + [ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" }, errors: ["some error"] }', + ], + ), + + // object-with-config + { + code: getTestCases(['"foo"']), + options: [{ require: 'objects-with-config' }], + name: 'shorthand string (valid | require: objects-with-config)', + }, + { + code: getTestCases(['"foo"', '"bar"']), + options: [{ require: 'objects-with-config' }], + name: 'shorthand strings (valid | require: objects-with-config)', + }, + { + code: getTestCases(['"foo"', '"bar"', '{ code: "foo" }']), + options: [{ require: 'objects-with-config' }], + name: 'object without config (valid | require: objects-with-config)', + }, + { + code: getTestCases([ + '{ code: "foo" }', + '{ code: "bar", name: "my test" }', + ]), + options: [{ require: 'objects-with-config' }], + name: 'object with name and no config (valid | require: objects-with-config)', + }, + { + code: getTestCases([ + '{ code: "foo" }', + '{ code: "foo", name: "my test", options: ["bar"] }', + '{ code: "bar", name: "my other test" }', + ]), + options: [{ require: 'objects-with-config' }], + name: 'object with options (valid | require: objects-with-config)', + }, + { + code: getTestCases([ + '{ code: "foo" }', + '{ code: "foo", name: "my test", settings: { setting1: "bar" } }', + '{ code: "bar", name: "my other test" }', + ]), + options: [{ require: 'objects-with-config' }], + name: 'object with settings (valid | require: objects-with-config)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'objects-with-config' }], + name: 'object with options and settings (valid | require: objects-with-config)', + }, + { + code: getTestCases([], ['{ code: "foo", errors: ["some error"] }']), + options: [{ require: 'objects-with-config' }], + name: 'object without config (invalid | require: objects-with-config)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "bar", name: "my test", errors: ["some error"] }', + ], + ), + options: [{ require: 'objects-with-config' }], + name: 'object with name and no config (invalid | require: objects-with-config)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "foo", name: "my test", options: ["bar"], errors: ["some error"] }', + '{ code: "bar", name: "my other test", errors: ["some error"] }', + ], + ), + options: [{ require: 'objects-with-config' }], + name: 'object with options (invalid | require: objects-with-config)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", errors: ["some error"] }', + '{ code: "foo", name: "my test", settings: { setting1: "bar" }, errors: ["some error"] }', + '{ code: "bar", name: "my other test", errors: ["some error"] }', + ], + ), + options: [{ require: 'objects-with-config' }], + name: 'object with settings (invalid | require: objects-with-config)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" }, errors: ["some error"] }', + ], + ), + options: [{ require: 'objects-with-config' }], + name: 'object with options and settings (invalid | require: objects-with-config)', + }, + + // objects + { + code: getTestCases(['"foo"']), + options: [{ require: 'objects' }], + name: 'shorthand string (valid | require: objects)', + }, + { + code: getTestCases(['"foo"', '"bar"']), + options: [{ require: 'objects' }], + name: 'shorthand strings (valid | require: objects)', + }, + { + code: getTestCases(['"foo"', '{ code: "bar", name: "my test" }']), + options: [{ require: 'objects' }], + name: 'object without config (valid | require: objects)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my first test" }', + '{ code: "foo", name: "my second test", options: ["bar"] }', + '{ code: "bar", name: "my third test" }', + ]), + options: [{ require: 'objects' }], + name: 'object with options (valid | require: objects)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my first test" }', + '{ code: "foo", name: "my second test", settings: { setting1: "bar" } }', + '{ code: "bar", name: "my other test" }', + ]), + options: [{ require: 'objects' }], + name: 'object with settings (valid | require: objects)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'objects' }], + name: 'object with options and settings (valid | require: objects)', + }, + { + code: getTestCases( + [], + ['{ code: "bar", name: "my test", errors: ["some error"] }'], + ), + options: [{ require: 'objects' }], + name: 'object without config (invalid | require: objects)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my first test", errors: ["some error"] }', + '{ code: "foo", name: "my second test", options: ["bar"], errors: ["some error"] }', + '{ code: "bar", name: "my third test", errors: ["some error"] }', + ], + ), + options: [{ require: 'objects' }], + name: 'object with options (invalid | require: objects)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my first test", errors: ["some error"] }', + '{ code: "foo", name: "my second test", settings: { setting1: "bar" }, errors: ["some error"] }', + '{ code: "bar", name: "my other test", errors: ["some error"] }', + ], + ), + options: [{ require: 'objects' }], + name: 'object with settings (invalid | require: objects)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" }, errors: ["some error"] }', + ], + ), + options: [{ require: 'objects' }], + name: 'object with options and settings (invalid | require: objects)', + }, + + // always + { + code: getTestCases(['{ code: "bar", name: "my test" }']), + options: [{ require: 'always' }], + name: 'object without config (valid | require: always)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my first test" }', + '{ code: "foo", name: "my second test", options: ["bar"] }', + '{ code: "bar", name: "my third test" }', + ]), + options: [{ require: 'always' }], + name: 'object with options (valid | require: always)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my first test" }', + '{ code: "foo", name: "my second test", settings: { setting1: "bar" } }', + '{ code: "bar", name: "my third test" }', + ]), + options: [{ require: 'always' }], + name: 'object with settings (valid | require: always)', + }, + { + code: getTestCases([ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'always' }], + name: 'object with options and settings (valid | require: always)', + }, + { + code: getTestCases( + [], + ['{ code: "bar", name: "my test", errors: ["some error"] }'], + ), + options: [{ require: 'always' }], + name: 'object without config (invalid | require: always)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my first test", errors: ["some error"] }', + '{ code: "foo", name: "my second test", options: ["bar"], errors: ["some error"] }', + '{ code: "bar", name: "my third test", errors: ["some error"] }', + ], + ), + options: [{ require: 'always' }], + name: 'object with options (invalid | require: always)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my first test", errors: ["some error"] }', + '{ code: "foo", name: "my second test", settings: { setting1: "bar" }, errors: ["some error"] }', + '{ code: "bar", name: "my third test", errors: ["some error"] }', + ], + ), + options: [{ require: 'always' }], + name: 'object with settings (invalid | require: always)', + }, + { + code: getTestCases( + [], + [ + '{ code: "foo", name: "my test", options: ["bar"], settings: { setting1: "bar" }, errors: ["some error"] }', + ], + ), + options: [{ require: 'always' }], + name: 'object with options and settings (invalid | require: always)', + }, + ], + + invalid: [ + // default (objects-with-config) + { + code: getTestCases(['{ code: "foo", options: ["bar"] }']), + errors: [objectsWithConfigError], + }, + { + code: getTestCases(['{ code: "foo", settings: { setting1: "bar" } }']), + errors: [objectsWithConfigError], + }, + { + code: getTestCases([ + '{ code: "foo", options: ["bar"], settings: { setting1: "bar" } }', + ]), + errors: [objectsWithConfigError], + }, + // (objects-with-config) + { + code: getTestCases(['{ code: "foo", options: ["bar"] }']), + options: [{ require: 'objects-with-config' }], + errors: [objectsWithConfigError], + name: 'object with options (require: objects-with-config)', + }, + { + code: getTestCases(['{ code: "foo", settings: { setting1: "bar" } }']), + options: [{ require: 'objects-with-config' }], + errors: [objectsWithConfigError], + name: 'object with settings (require: objects-with-config)', + }, + { + code: getTestCases([ + '{ code: "foo", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'objects-with-config' }], + errors: [objectsWithConfigError], + name: 'object with options and settings (require: objects-with-config)', + }, + // (objects) + { + code: getTestCases(['{ code: "foo" }']), + options: [{ require: 'objects' }], + errors: [objectsError], + name: 'object without config (require: objects)', + }, + { + code: getTestCases(['{ code: "foo", options: ["bar"] }']), + options: [{ require: 'objects' }], + errors: [objectsError], + name: 'object with options (require: objects)', + }, + { + code: getTestCases(['{ code: "foo", settings: { setting1: "bar" } }']), + options: [{ require: 'objects' }], + errors: [objectsError], + name: 'object with settings (require: objects)', + }, + { + code: getTestCases([ + '{ code: "foo", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'objects' }], + errors: [objectsError], + name: 'object with options and settings (require: objects)', + }, + // (always) + { + code: getTestCases(['"foo"']), + options: [{ require: 'always' }], + errors: [alwaysError], + name: 'shorthand string (require: always)', + }, + { + code: getTestCases(['{ code: "foo" }']), + options: [{ require: 'always' }], + errors: [alwaysError], + name: 'object without config (require: always)', + }, + { + code: getTestCases(['{ code: "foo", options: ["bar"] }']), + options: [{ require: 'always' }], + errors: [alwaysError], + name: 'object with options (require: always)', + }, + { + code: getTestCases(['{ code: "foo", settings: { setting1: "bar" } }']), + options: [{ require: 'always' }], + errors: [alwaysError], + name: 'object with settings (require: always)', + }, + { + code: getTestCases([ + '{ code: "foo", options: ["bar"], settings: { setting1: "bar" } }', + ]), + options: [{ require: 'always' }], + errors: [alwaysError], + name: 'object with options and settings (require: always)', + }, + ], +}); diff --git a/tests/lib/rules/test-case-property-ordering.ts b/tests/lib/rules/test-case-property-ordering.ts index e3dae7b3..46dfb04b 100644 --- a/tests/lib/rules/test-case-property-ordering.ts +++ b/tests/lib/rules/test-case-property-ordering.ts @@ -57,6 +57,7 @@ ruleTester.run('test-case-property-ordering', rule, { }); `, options: [['code', 'errors', 'options', 'output', 'parserOptions']], + name: 'custom order (options: code, errors, options, output, parserOptions)', }, ` new NotRuleTester().run('foo', bar, { @@ -159,6 +160,7 @@ ruleTester.run('test-case-property-ordering', rule, { 'The properties of a test case should be placed in a consistent order: [code, options, output].', }, ], + name: 'custom order (options: code, errors, options, output, parserOptions)', }, { code: ` diff --git a/tests/lib/rules/test-case-shorthand-strings.ts b/tests/lib/rules/test-case-shorthand-strings.ts index 729f72e8..30a70d04 100644 --- a/tests/lib/rules/test-case-shorthand-strings.ts +++ b/tests/lib/rules/test-case-shorthand-strings.ts @@ -55,10 +55,12 @@ ruleTester.run('test-case-shorthand-strings', rule, { { code: getTestCases(['"foo"']), options: ['as-needed'], + name: 'single string (options: as-needed)', }, { code: getTestCases(['"foo"', '"bar"']), options: ['as-needed'], + name: 'multiple strings (options: as-needed)', }, { code: getTestCases([ @@ -67,6 +69,7 @@ ruleTester.run('test-case-shorthand-strings', rule, { '{ code: "foo", options: ["bar"] }', ]), options: ['as-needed'], + name: 'mixed strings and object with options (options: as-needed)', }, { code: getTestCases([ @@ -75,38 +78,46 @@ ruleTester.run('test-case-shorthand-strings', rule, { '{ code: "foo", parserOptions: ["bar"] }', ]), options: ['as-needed'], + name: 'mixed strings and object with parserOptions (options: as-needed)', }, { code: getTestCases(['`foo`']), options: ['as-needed'], + name: 'template string (options: as-needed)', }, { code: getTestCases(['tag`foo`']), options: ['as-needed'], + name: 'tagged template string (options: as-needed)', }, // never { code: getTestCases(['{ code: "foo" }', '{ code: "bar" }']), options: ['never'], + name: 'objects (options: never)', }, { code: getTestCases(['notAString', '{ code: "bar" }']), options: ['never'], + name: 'variable and object (options: never)', }, { code: getTestCases(['notAString()', '{ code: "bar" }']), options: ['never'], + name: 'function call and object (options: never)', }, // consistent { code: getTestCases(['"foo"', '"bar"']), options: ['consistent'], + name: 'multiple strings (options: consistent)', }, { code: getTestCases(['{ code: "foo" }', '{ code: "bar" }']), options: ['consistent'], + name: 'multiple objects (options: consistent)', }, { code: getTestCases([ @@ -114,16 +125,19 @@ ruleTester.run('test-case-shorthand-strings', rule, { '{ code: "bar", options: ["foo"] }', ]), options: ['consistent'], + name: 'multiple objects with options (options: consistent)', }, { code: getTestCases(['"foo"', "'bar'", '`baz`']), options: ['consistent'], + name: 'multiple types of strings (options: consistent)', }, // consistent-as-needed { code: getTestCases(['"foo"', '"bar"']), options: ['consistent-as-needed'], + name: 'multiple strings (options: consistent-as-needed)', }, { code: getTestCases([ @@ -131,10 +145,12 @@ ruleTester.run('test-case-shorthand-strings', rule, { '{ code: "bar", options: ["foo"] }', ]), options: ['consistent-as-needed'], + name: 'multiple objects (options: consistent-as-needed)', }, { code: getTestCases(['"foo"', "'bar'", '`baz`']), options: ['consistent-as-needed'], + name: 'multiple types of strings (options: consistent-as-needed)', }, ` new NotRuleTester().run('foo', bar, { @@ -177,24 +193,28 @@ ruleTester.run('test-case-shorthand-strings', rule, { output: getTestCases(['{code: "foo"}']), options: ['never'], errors: [UNEXPECTED_SHORTHAND_ERROR], + name: 'string (options: never)', }, { code: getTestCases(['foo', '"bar"']), output: getTestCases(['foo', '{code: "bar"}']), options: ['never'], errors: [UNEXPECTED_SHORTHAND_ERROR], + name: 'multiple strings (options: never)', }, { code: getTestCases(['`foo`']), output: getTestCases(['{code: `foo`}']), options: ['never'], errors: [UNEXPECTED_SHORTHAND_ERROR], + name: 'template string (options: never)', }, { code: getTestCases(['"foo"']) + getTestCases(['"foo"']), output: getTestCases(['{code: "foo"}']) + getTestCases(['{code: "foo"}']), options: ['never'], errors: [UNEXPECTED_SHORTHAND_ERROR, UNEXPECTED_SHORTHAND_ERROR], + name: 'multiple ruletester calls with strings (options: never)', }, // consistent @@ -215,6 +235,7 @@ ruleTester.run('test-case-shorthand-strings', rule, { output: getTestCases(['"foo"', '"bar"']), options: ['consistent-as-needed'], errors: [EXPECTED_SHORTHAND_ERROR, EXPECTED_SHORTHAND_ERROR], + name: 'multiple objects (options: consistent-as-needed)', }, { code: getTestCases([ @@ -229,6 +250,7 @@ ruleTester.run('test-case-shorthand-strings', rule, { ]), options: ['consistent-as-needed'], errors: [UNEXPECTED_SHORTHAND_ERROR, UNEXPECTED_SHORTHAND_ERROR], + name: 'multiple strings and object (options: consistent-as-needed)', }, { code: getTestCases([ @@ -243,6 +265,7 @@ ruleTester.run('test-case-shorthand-strings', rule, { ]), options: ['consistent-as-needed'], errors: [UNEXPECTED_SHORTHAND_ERROR, UNEXPECTED_SHORTHAND_ERROR], + name: 'multiple strings and object in between (options: consistent-as-needed)', }, ], });