@@ -4,168 +4,27 @@ const fs = require('fs')
4
4
const assert = require ( 'assert' )
5
5
const path = require ( 'path' )
6
6
7
- const RuleTester = require ( 'eslint' ) . RuleTester
8
- const ruleTester = new RuleTester ( { env : { es2020 : true } , parserOptions : { sourceType : 'module' } } )
9
-
10
- function rulesFromDir ( dir ) {
11
- try {
12
- return fs . readdirSync ( `./${ dir } ` ) . map ( f => path . basename ( f , path . extname ( f ) ) )
13
- } catch {
14
- return [ ]
15
- }
16
- }
17
-
18
- function makeTitle ( name ) {
19
- return name
20
- . replace ( / - / g, ' ' )
21
- . replace ( / \w \S * / g, x => x . charAt ( 0 ) . toUpperCase ( ) + x . substr ( 1 ) )
22
- . replace ( / \b ( T h e | A n ? | A n d | T o | I n | O n | W i t h ) \b / g, x => x . toLowerCase ( ) )
23
- . replace ( / \b ( D o m | H t m l | J s ) \b / g, x => x . toUpperCase ( ) )
24
- }
25
-
26
- function * extractCodeblocks ( lines ) {
27
- let inCodeBlock = false
28
- let codeLines = [ ]
29
- let startLine = 0
30
- let endLine = 0
31
- let lang = ''
32
- for ( const i in lines ) {
33
- const line = lines [ i ]
34
- if ( ! inCodeBlock && line . startsWith ( '```' ) ) {
35
- lang = line . slice ( 3 )
36
- startLine = i
37
- codeLines = [ ]
38
- inCodeBlock = true
39
- continue
40
- } else if ( inCodeBlock && line . startsWith ( '```' ) ) {
41
- endLine = i
42
- yield { code : codeLines , startLine, endLine, lang}
43
- inCodeBlock = false
44
- continue
45
- }
46
- if ( inCodeBlock ) {
47
- codeLines . push ( line )
48
- }
49
- }
50
- }
51
-
52
7
describe ( 'smoke tests' , ( ) => {
53
- it ( 'has file for each exported rule and rule for each exported file' , ( ) => {
54
- assert . deepStrictEqual (
55
- Object . keys ( config . rules ) ,
56
- rulesFromDir ( 'lib/rules' ) ,
57
- 'Expected lib/rules/*.js to be inside lib/index.js#rules'
58
- )
59
- } )
60
-
61
- it ( 'has export for each config and config for each import' , ( ) => {
62
- assert . deepStrictEqual (
63
- Object . keys ( config . configs ) ,
64
- rulesFromDir ( 'lib/configs' ) ,
65
- 'Expected lib/configs/*.js to be inside lib/index.js#configs'
66
- )
8
+ it ( 'ensure all rules in lib/rules are included in index' , ( ) => {
9
+ const exportedRules = new Set ( Object . keys ( config . rules ) )
10
+ const files = new Set ( fs . readdirSync ( './lib/rules' ) . map ( f => path . basename ( f , path . extname ( f ) ) ) )
11
+ assert . deepEqual ( files , exportedRules )
67
12
} )
68
13
69
- for ( const flavour in config . configs ) {
70
- describe ( `${ flavour } config` , ( ) => {
71
- it ( 'exports valid rules' , ( ) => {
72
- const exportedRules = new Set ( Object . keys ( config . rules ) )
73
- const ceRules = Object . keys ( config . configs [ flavour ] . rules ) . filter ( rule => rule . startsWith ( 'custom-elements/' ) )
74
- const violations = ceRules . filter ( rule => ! exportedRules . has ( rule . replace ( / ^ c u s t o m - e l e m e n t s \/ / , '' ) ) )
75
- assert . deepStrictEqual ( violations , [ ] , 'All custom-elements/ rules should exist in lib/index.js#rules' )
76
- } )
77
- } )
78
- }
79
- } )
80
-
81
- describe ( 'test coverage' , ( ) => {
82
- it ( 'has tests for each rule and rules for each test' , ( ) => {
83
- const tests = rulesFromDir ( 'tests' ) . filter ( name => name !== 'check-rules' )
84
- assert . deepStrictEqual ( rulesFromDir ( 'lib/rules' ) , tests , 'Expected lib/rules/*.js to have same files as tests/*.js' )
14
+ it ( 'exports every config in lib/config as .configs' , ( ) => {
15
+ const exportedConfigs = new Set ( Object . keys ( config . configs ) )
16
+ const files = new Set ( fs . readdirSync ( './lib/configs' ) . map ( f => path . basename ( f , path . extname ( f ) ) ) )
17
+ assert . deepEqual ( files , exportedConfigs )
85
18
} )
86
- } )
87
19
88
- describe ( 'documentation' , ( ) => {
89
- it ( 'has rule for each doc file and doc file for each rule' , ( ) => {
90
- assert . deepStrictEqual ( rulesFromDir ( 'docs/rules' ) , rulesFromDir ( 'lib/rules' ) )
91
- } )
92
-
93
- it ( 'has readme link to each doc' , ( ) => {
94
- const contents = fs . readFileSync ( `./README.md` , 'utf-8' ) . split ( '\n' )
95
- const i = contents . indexOf ( '### Rules' )
96
- let n = contents . findIndex ( ( line , index ) => index > i && line . startsWith ( '#' ) )
97
- if ( n < i ) n = contents . length
98
- const ruleLinks = contents
99
- . slice ( i + 1 , n )
100
- . filter ( Boolean )
101
- . map ( x => x . trim ( ) )
102
- const desiredRuleLinks = rulesFromDir ( 'docs/rules' ) . map ( rule => `- [${ makeTitle ( rule ) } ](./docs/rules/${ rule } .md)` )
103
- assert . deepStrictEqual ( desiredRuleLinks , ruleLinks , 'Expected each rule in docs/rules/*.md to have README link' )
104
- } )
105
-
106
- for ( const doc of rulesFromDir ( 'docs/rules' ) ) {
107
- it ( `has correct headings in ${ doc } .md` , ( ) => {
108
- const contents = fs . readFileSync ( `./docs/rules/${ doc } .md` , 'utf-8' ) . split ( '\n' )
109
- let consume = true
110
- const headings = contents . filter ( line => {
111
- // Discard lines that aren't headers or thumbs
112
- if ( ! ( line . startsWith ( '#' ) || line . startsWith ( '\ud83d' ) ) ) return false
113
- // Ignore all sub headings/thumbs between `### Options` and `## When Not To Use It`
114
- if ( line === '### Options' ) {
115
- consume = false
116
- return true
117
- } else if ( line === '## When Not To Use It' ) {
118
- consume = true
119
- }
120
- return consume
121
- } )
122
- const desiredHeadings = [
123
- `# ${ makeTitle ( doc ) } ` ,
124
- '## Rule Details' ,
125
- '👎 Examples of **incorrect** code for this rule:' ,
126
- '👍 Examples of **correct** code for this rule:' ,
127
- config . rules ?. [ doc ] ?. schema ?. length ? '### Options' : '' ,
128
- '## When Not To Use It' ,
129
- '## Version'
130
- ] . filter ( Boolean )
131
- assert . deepStrictEqual ( headings , desiredHeadings , 'Expected doc to have correct headings' )
132
- } )
133
-
134
- it ( `has working examples in ${ doc } .md` , ( ) => {
135
- const rules = { valid : [ ] , invalid : [ ] }
136
- const lines = fs . readFileSync ( `./docs/rules/${ doc } .md` , 'utf-8' ) . split ( '\n' )
137
-
138
- for ( const { code, startLine} of extractCodeblocks ( lines ) ) {
139
- const validIndex = lines . lastIndexOf ( '👍 Examples of **correct** code for this rule:' , startLine )
140
- const invalidIndex = lines . lastIndexOf ( '👎 Examples of **incorrect** code for this rule:' , startLine )
141
-
142
- if ( validIndex === invalidIndex ) {
143
- continue
144
- }
145
-
146
- let filename = ''
147
- if ( code [ 0 ] . match ( / \s * \/ \/ .* \. [ j t ] s $ / ) ) {
148
- filename = code [ 0 ] . replace ( '// ' , '' ) . trim ( )
149
- }
150
-
151
- if ( validIndex > invalidIndex ) {
152
- rules . valid . push ( { code : code . join ( '\n' ) } )
153
- } else {
154
- rules . invalid . push ( { code : code . join ( '\n' ) , errors : 1 , filename} )
20
+ it ( 'exports valid rules in each config' , ( ) => {
21
+ const exportedRules = new Set ( Object . keys ( config . rules ) )
22
+ for ( const flavour in config . configs ) {
23
+ for ( const rule in config . configs [ flavour ] . rules ) {
24
+ if ( rule . startsWith ( 'github/' ) ) {
25
+ assert ( exportedRules . has ( rule . replace ( / ^ g i t h u b \/ / , '' ) ) , `rule ${ rule } is not a valid rule` )
155
26
}
156
27
}
157
-
158
- // eslint-disable-next-line import/no-dynamic-require
159
- const rule = require ( `../lib/rules/${ doc } ` )
160
- ruleTester . run ( doc , rule , rules )
161
- } )
162
-
163
- it ( `has javascript examples in ${ doc } .md` , ( ) => {
164
- const lines = fs . readFileSync ( `./docs/rules/${ doc } .md` , 'utf-8' ) . split ( '\n' )
165
- assert (
166
- Array . from ( extractCodeblocks ( lines ) ) . find ( x => x . lang === 'js' ) ,
167
- 'Expected documentation to include a JavaScript codeblock'
168
- )
169
- } )
170
- }
28
+ }
29
+ } )
171
30
} )
0 commit comments