@@ -4,168 +4,27 @@ const fs = require('fs')
44const assert = require ( 'assert' )
55const path = require ( 'path' )
66
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-
527describe ( '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 )
6712 } )
6813
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 )
8518 } )
86- } )
8719
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` )
15526 }
15627 }
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+ } )
17130} )
0 commit comments