From a67b22dd076d9c46f01580c18e94b6d1bd2ee230 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 3 Jun 2024 19:16:39 -0400 Subject: [PATCH 1/2] test(eslint-plugin): add docs test for Correct and Incorrect blocks --- packages/eslint-plugin/tests/docs.test.ts | 84 ++++++++++++++++++++--- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index c3f08de4bbda..49f0b4fefa3f 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -137,6 +137,39 @@ describe('Validating rule docs', () => { const rulesWithComplexOptions = new Set(['array-type', 'member-ordering']); + const rulesWithComplexOptionHeadings = new Set([ + 'ban-ts-comment', + 'ban-types', + 'consistent-type-exports', + 'consistent-type-imports', + 'explicit-function-return-type', + 'explicit-member-accessibility', + 'explicit-module-boundary-types', + 'no-base-to-string', + 'no-confusing-void-expression', + 'no-duplicate-type-constituents', + 'no-empty-interface', + 'no-explicit-any', + 'no-floating-promises', + 'no-inferrable-types', + 'no-invalid-void-type', + 'no-meaningless-void-operator', + 'no-misused-promises', + 'no-type-alias', + 'no-unnecessary-condition', + 'no-unnecessary-type-assertion', + 'parameter-properties', + 'prefer-nullish-coalescing', + 'prefer-optional-chain', + 'prefer-string-starts-ends-with', + 'promise-function-async', + 'restrict-template-expressions', + 'strict-boolean-expressions', + 'switch-exhaustiveness-check', + 'switch-exhaustiveness-check', + 'unbound-method', + ]); + it('All rules must have a corresponding rule doc', () => { const files = fs .readdirSync(docsRoot) @@ -238,22 +271,55 @@ describe('Validating rule docs', () => { !rule.meta.docs?.extendsBaseRule && rule.meta.type !== 'layout' ) { - test('each rule option should be mentioned in a heading', () => { - const headingTextAfterOptions = headings - .slice(headings.findIndex(header => header.text === 'Options')) - .map(header => header.text) - .join('\n'); + describe('rule options', () => { + const headingsAfterOptions = headings.slice( + headings.findIndex(header => header.text === 'Options'), + ); for (const schemaItem of schema) { if (schemaItem.type === 'object') { for (const property of Object.keys( schemaItem.properties as object, )) { - if (!headingTextAfterOptions.includes(`\`${property}\``)) { - throw new Error( - `At least one header should include \`${property}\`.`, + test(property, () => { + const correspondingHeadingIndex = + headingsAfterOptions.findIndex(heading => + heading.text.includes(`\`${property}\``), + ); + + if (correspondingHeadingIndex === -1) { + throw new Error( + `At least one header should include \`${property}\`.`, + ); + } + + if (rulesWithComplexOptionHeadings.has(ruleName)) { + return; + } + + const relevantChildren = tokens.slice( + tokens.indexOf( + headingsAfterOptions[correspondingHeadingIndex], + ), + tokens.indexOf( + headingsAfterOptions[correspondingHeadingIndex + 1], + ), ); - } + + for (const rawTab of [ + ``, + ``, + ]) { + if ( + !relevantChildren.some( + child => + child.type === 'html' && child.raw.includes(rawTab), + ) + ) { + throw new Error(`Missing option example tab: ${rawTab}`); + } + } + }); } } } From 9996d9aab6586ff3b7d8df92222cb0a1a8aef94c Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 4 Jun 2024 17:11:25 -0400 Subject: [PATCH 2/2] the TODO comment --- packages/eslint-plugin/tests/docs.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 49f0b4fefa3f..705b0dfa6823 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -137,6 +137,7 @@ describe('Validating rule docs', () => { const rulesWithComplexOptions = new Set(['array-type', 'member-ordering']); + // TODO: whittle this list down to as few as possible const rulesWithComplexOptionHeadings = new Set([ 'ban-ts-comment', 'ban-types',