Skip to content

Commit

Permalink
Add basic tests of rule docs (#1573)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
bmish and sindresorhus committed Nov 4, 2021
1 parent f0dec1b commit 5625dab
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 11 deletions.
2 changes: 1 addition & 1 deletion docs/rules/custom-error-definition.md
@@ -1,4 +1,4 @@
# Enforce correct Error subclassing
# Enforce correct `Error` subclassing

Enforces the only valid way of `Error` subclassing. It works with any super class that ends in `Error`.

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/new-for-builtins.md
@@ -1,4 +1,4 @@
# Enforce the use of `new` for all builtins, except `String`, `Number` and `Boolean`
# Enforce the use of `new` for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`

They work the same, but `new` should be preferred for consistency with other constructors.

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/no-null.md
@@ -1,4 +1,4 @@
# Disallow the use of the `null` literal.
# Disallow the use of the `null` literal

Disallow the use of the `null` literal, to encourage using `undefined` instead.

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/no-useless-spread.md
@@ -1,4 +1,4 @@
# Disallow useless spread
# Disallow unnecessary spread

- Using spread syntax in the following cases is unnecessary:

Expand Down
4 changes: 3 additions & 1 deletion docs/rules/prefer-dom-node-dataset.md
@@ -1,4 +1,6 @@
# Prefer using [`.dataset`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset) on DOM elements over `.setAttribute(…)`
# Prefer using `.dataset` on DOM elements over `.setAttribute(…)`

Use [`.dataset`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset) on DOM elements over `.setAttribute(…)`.

This rule is fixable.

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/prefer-modern-dom-apis.md
@@ -1,4 +1,4 @@
# Prefer modern DOM APIs
# Prefer `.before()` over `.insertBefore()`, `.replaceWith()` over `.replaceChild()`, prefer one of `.before()`, `.after()`, `.append()` or `.prepend()` over `insertAdjacentText()` and `insertAdjacentElement()`

Enforces the use of:

Expand Down
4 changes: 2 additions & 2 deletions docs/rules/prefer-number-properties.md
@@ -1,4 +1,4 @@
# Prefer `Number` static properties over global ones.
# Prefer `Number` static properties over global ones

Enforces the use of:

Expand Down Expand Up @@ -77,7 +77,7 @@ const isPositiveZero = value => value === 0 && 1 / value === Number.POSITIVE_INF
const isNegativeZero = value => value === 0 && 1 / value === Number.NEGATIVE_INFINITY;
```

# Options
## Options

Type: `object`

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/prefer-prototype-methods.md
@@ -1,4 +1,4 @@
# Prefer borrowing methods from the prototype instead of methods from an instance
# Prefer borrowing methods from the prototype instead of the instance

When “borrowing” a method from `Array` or `Object`, it‘s clearer to get it from the prototype than from an instance.

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/prefer-switch.md
Expand Up @@ -48,7 +48,7 @@ switch (foo) {
}
```

### `options`
## Options

Type: `object`

Expand Down
3 changes: 3 additions & 0 deletions scripts/create-rule.mjs
Expand Up @@ -6,6 +6,7 @@ import {fileURLToPath} from 'node:url';
import enquirer from 'enquirer';
import {template} from 'lodash-es';
import execa from 'execa';
import ruleDescriptionToDocumentTitle from '../test/utils/rule-description-to-document-title.mjs';

const dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.join(dirname, '..');
Expand Down Expand Up @@ -138,6 +139,8 @@ function updateRecommended(id) {
data.fixableType = false;
}

data.docTitle = ruleDescriptionToDocumentTitle(data.description);

const {id} = data;

checkFiles(id);
Expand Down
2 changes: 1 addition & 1 deletion scripts/template/documentation.md.jst
@@ -1,4 +1,4 @@
# <%= description %>
# <%= docTitle %>

<!-- More detailed description. Remove this comment. -->
<% if (fixableType) { %>
Expand Down
59 changes: 59 additions & 0 deletions test/package.mjs
Expand Up @@ -4,6 +4,7 @@ import {createRequire} from 'node:module';
import test from 'ava';
import {ESLint} from 'eslint';
import index from '../index.js';
import ruleDescriptionToDocumentTitle from './utils/rule-description-to-document-title.mjs';

const require = createRequire(import.meta.url);
let ruleFiles;
Expand Down Expand Up @@ -32,6 +33,32 @@ const testSorted = (t, actualOrder, sourceName) => {
}
};

/**
Get list of named options from a JSON schema (used for rule schemas).
@param {object | Array} jsonSchema - The JSON schema to check.
@returns {string[]} A list of named options.
*/
function getNamedOptions(jsonSchema) {
if (!jsonSchema) {
return [];
}

if (Array.isArray(jsonSchema)) {
return jsonSchema.flatMap(item => getNamedOptions(item));
}

if (jsonSchema.items) {
return getNamedOptions(jsonSchema.items);
}

if (jsonSchema.properties) {
return Object.keys(jsonSchema.properties);
}

return [];
}

test('Every rule is defined in index file in alphabetical order', t => {
for (const file of ruleFiles) {
const name = path.basename(file, '.js');
Expand Down Expand Up @@ -148,3 +175,35 @@ test('Every deprecated rules listed in docs/deprecated-rules.md', async t => {
t.true(rule.meta.deprecated, `${name} meta.deprecated should be true`);
}
});

test('Every rule has a doc with the appropriate content', t => {
for (const ruleFile of ruleFiles) {
const ruleName = path.basename(ruleFile, '.js');
const rule = index.rules[ruleName];
const documentPath = path.join('docs/rules', `${ruleName}.md`);
const documentContents = fs.readFileSync(documentPath, 'utf8');
const documentLines = documentContents.split('\n');

// Check title.
const expectedTitle = `# ${ruleDescriptionToDocumentTitle(rule.meta.docs.description)}`;
t.is(documentLines[0], expectedTitle, `${ruleName} includes the rule description in title`);

// Check if the rule has configuration options.
if (
(Array.isArray(rule.meta.schema) && rule.meta.schema.length > 0)
|| (typeof rule.meta.schema === 'object' && Object.keys(rule.meta.schema).length > 0)
) {
// Should have an options section header:
t.true(documentContents.includes('## Options'), `${ruleName} should have an "## Options" section`);

// Ensure all configuration options are mentioned.
for (const namedOption of getNamedOptions(rule.meta.schema)) {
t.true(documentContents.includes(namedOption), `${ruleName} should mention the \`${namedOption}\` option`);
}
} else {
// Should NOT have any options/config section headers:
t.false(documentContents.includes('# Options'), `${ruleName} should not have an "Options" section`);
t.false(documentContents.includes('# Config'), `${ruleName} should not have a "Config" section`);
}
}
});
14 changes: 14 additions & 0 deletions test/utils/rule-description-to-document-title.mjs
@@ -0,0 +1,14 @@
/**
From a rule's description, generate the intended title for its rule doc.
@param {string} description - rule description
@returns {string} title for rule doc
*/
export default function ruleDescriptionToDocumentTitle(description) {
let title = description.charAt(0).toUpperCase() + description.slice(1); // Capitalize first letter.
if (title.endsWith('.')) {
title = title.slice(0, -1); // Remove any ending period.
}

return title;
}

0 comments on commit 5625dab

Please sign in to comment.