/
index.js
164 lines (141 loc) · 6.55 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* eslint-env mocha */
'use strict';
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const plugin = require('..');
const ruleFiles = fs.readdirSync(path.resolve(__dirname, '../lib/rules/'))
.map((f) => path.basename(f, '.js'));
describe('all rule files should be exported by the plugin', () => {
ruleFiles.forEach((ruleName) => {
it(`should export ${ruleName}`, () => {
assert.equal(
plugin.rules[ruleName],
require(path.join('../lib/rules', ruleName)) // eslint-disable-line global-require, import/no-dynamic-require
);
});
});
});
describe('rule documentation files have the correct content', () => {
const MESSAGES = {
deprecated: '❌ This rule is deprecated.',
fixable: '🔧 This rule is automatically fixable using the `--fix` [flag](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix) on the command line.',
hasSuggestions: '💡 This rule provides editor [suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).',
};
ruleFiles.forEach((ruleName) => {
it(ruleName, () => {
const rule = plugin.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 = `# ${rule.meta.docs.description} (react/${ruleName})`;
assert.strictEqual(documentLines[0], expectedTitle, 'includes the rule description and name in title');
// Decide which notices should be shown at the top of the doc.
const expectedNotices = [];
const unexpectedNotices = [];
if (rule.meta.deprecated) {
expectedNotices.push('deprecated');
} else {
unexpectedNotices.push('deprecated');
}
if (rule.meta.fixable) {
expectedNotices.push('fixable');
} else {
unexpectedNotices.push('fixable');
}
if (rule.meta.hasSuggestions) {
expectedNotices.push('hasSuggestions');
} else {
unexpectedNotices.push('hasSuggestions');
}
// Ensure that expected notices are present in the correct order.
let currentLineNumber = 1;
expectedNotices.forEach((expectedNotice) => {
assert.strictEqual(documentLines[currentLineNumber], '', `includes blank line ahead of ${expectedNotice} notice`);
if (expectedNotice === 'deprecated' && documentLines[currentLineNumber + 1] !== MESSAGES[expectedNotice] && documentLines[currentLineNumber + 1].startsWith(MESSAGES[expectedNotice])) {
// Allow additional rule-specific information at the end of the deprecation notice line.
assert.ok(true, `includes ${expectedNotice} notice`);
} else {
// Otherwise, just check the whole line.
assert.strictEqual(documentLines[currentLineNumber + 1], MESSAGES[expectedNotice], `includes ${expectedNotice} notice`);
}
currentLineNumber += 2;
});
// Ensure that unexpected notices are not present.
unexpectedNotices.forEach((unexpectedNotice) => {
assert.ok(!documentContents.includes(MESSAGES[unexpectedNotice]), `does not include unexpected ${unexpectedNotice} notice`);
});
// Check for Rule Details section.
assert.ok(documentContents.includes('## Rule Details'), 'should have a "## Rule Details" section');
// 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:
assert.ok(documentContents.includes('## Rule Options'), 'should have a "## Rule Options" section');
} else {
// Should NOT have any options section header:
assert.ok(!documentContents.includes('## Rule Options'), 'should not have a "## Rule Options" section');
}
});
});
});
describe('deprecated rules', () => {
it('marks all deprecated rules as deprecated', () => {
ruleFiles.forEach((ruleName) => {
const inDeprecatedRules = Boolean(plugin.deprecatedRules[ruleName]);
const isDeprecated = plugin.rules[ruleName].meta.deprecated;
if (inDeprecatedRules) {
assert(isDeprecated, `${ruleName} metadata should mark it as deprecated`);
} else {
assert(!isDeprecated, `${ruleName} metadata should not mark it as deprecated`);
}
});
});
});
describe('configurations', () => {
it('should export a ‘recommended’ configuration', () => {
const configName = 'recommended';
assert(plugin.configs[configName]);
Object.keys(plugin.configs[configName].rules).forEach((ruleName) => {
assert.ok(ruleName.startsWith('react/'));
const subRuleName = ruleName.slice('react/'.length);
assert(plugin.rules[subRuleName]);
});
ruleFiles.forEach((ruleName) => {
const inRecommendedConfig = !!plugin.configs[configName].rules[`react/${ruleName}`];
const isRecommended = plugin.rules[ruleName].meta.docs[configName];
if (inRecommendedConfig) {
assert(isRecommended, `${ruleName} metadata should mark it as recommended`);
} else {
assert(!isRecommended, `${ruleName} metadata should not mark it as recommended`);
}
});
});
it('should export an ‘all’ configuration', () => {
const configName = 'all';
assert(plugin.configs[configName]);
Object.keys(plugin.configs[configName].rules).forEach((ruleName) => {
assert.ok(ruleName.startsWith('react/'));
assert.equal(plugin.configs[configName].rules[ruleName], 2);
});
ruleFiles.forEach((ruleName) => {
const inDeprecatedRules = Boolean(plugin.deprecatedRules[ruleName]);
const inConfig = typeof plugin.configs[configName].rules[`react/${ruleName}`] !== 'undefined';
assert(inDeprecatedRules ^ inConfig); // eslint-disable-line no-bitwise
});
});
it('should export a \'jsx-runtime\' configuration', () => {
const configName = 'jsx-runtime';
assert(plugin.configs[configName]);
Object.keys(plugin.configs[configName].rules).forEach((ruleName) => {
assert.ok(ruleName.startsWith('react/'));
assert.equal(plugin.configs[configName].rules[ruleName], 0);
const inDeprecatedRules = Boolean(plugin.deprecatedRules[ruleName]);
const inConfig = typeof plugin.configs[configName].rules[ruleName] !== 'undefined';
assert(inDeprecatedRules ^ inConfig); // eslint-disable-line no-bitwise
});
});
});