]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/tests/bin/eslint.js
2 * @fileoverview Integration tests for the eslint.js executable.
8 const childProcess
= require ( "child_process" );
9 const fs
= require ( "fs" );
10 const assert
= require ( "chai" ). assert
;
11 const path
= require ( "path" );
13 const EXECUTABLE_PATH
= path
. resolve ( path
. join ( __dirname
, "../../bin/eslint.js" ));
16 * Returns a Promise for when a child process exits
17 * @param {ChildProcess} exitingProcess The child process
18 * @returns {Promise<number>} A Promise that fulfills with the exit code when the child process exits
20 function awaitExit ( exitingProcess
) {
21 return new Promise ( resolve
=> exitingProcess
. once ( "exit" , resolve
));
25 * Asserts that the exit code of a given child process will equal the given value.
26 * @param {ChildProcess} exitingProcess The child process
27 * @param {number} expectedExitCode The expected exit code of the child process
28 * @returns {Promise} A Promise that fulfills if the exit code ends up matching, and rejects otherwise.
30 function assertExitCode ( exitingProcess
, expectedExitCode
) {
31 return awaitExit ( exitingProcess
). then ( exitCode
=> {
32 assert
. strictEqual ( exitCode
, expectedExitCode
, `Expected an exit code of ${expectedExitCode} but got ${exitCode} .` );
37 * Returns a Promise for the stdout of a process.
38 * @param {ChildProcess} runningProcess The child process
39 * @returns {Promise<{stdout: string, stderr: string}>} A Promise that fulfills with all of the
40 * stdout and stderr output produced by the process when it exits.
42 function getOutput ( runningProcess
) {
46 runningProcess
. stdout
. on ( "data" , data
=> ( stdout
+= data
));
47 runningProcess
. stderr
. on ( "data" , data
=> ( stderr
+= data
));
48 return awaitExit ( runningProcess
). then (() => ({ stdout
, stderr
}));
51 describe ( "bin/eslint.js" , () => {
52 const forkedProcesses
= new Set ();
55 * Forks the process to run an instance of ESLint.
56 * @param {string[]} [args] An array of arguments
57 * @param {Object} [options] An object containing options for the resulting child process
58 * @returns {ChildProcess} The resulting child process
60 function runESLint ( args
, options
) {
61 const newProcess
= childProcess
. fork ( EXECUTABLE_PATH
, args
, Object
. assign ({ silent
: true }, options
));
63 forkedProcesses
. add ( newProcess
);
67 describe ( "reading from stdin" , () => {
68 it ( "has exit code 0 if no linting errors are reported" , () => {
69 const child
= runESLint ([ "--stdin" , "--no-eslintrc" ]);
71 child
. stdin
. write ( "var foo = bar; \n " );
73 return assertExitCode ( child
, 0 );
76 it ( "has exit code 0 if no linting errors are reported" , () => {
77 const child
= runESLint ([
81 "{'no-extra-semi': 2}" ,
87 const expectedOutput
= JSON
. stringify ([
94 fixableWarningCount
: 0 ,
95 output
: "var foo = bar; \n " ,
96 usedDeprecatedRules
: []
100 const exitCodePromise
= assertExitCode ( child
, 0 );
101 const stdoutPromise
= getOutput ( child
). then ( output
=> {
102 assert
. strictEqual ( output
. stdout
. trim (), expectedOutput
);
103 assert
. strictEqual ( output
. stderr
, "" );
106 child
. stdin
. write ( "var foo = bar;; \n " );
109 return Promise
. all ([ exitCodePromise
, stdoutPromise
]);
112 it ( "has exit code 1 if a syntax error is thrown" , () => {
113 const child
= runESLint ([ "--stdin" , "--no-eslintrc" ]);
115 child
. stdin
. write ( "This is not valid JS syntax. \n " );
117 return assertExitCode ( child
, 1 );
120 it ( "has exit code 1 if a linting error occurs" , () => {
121 const child
= runESLint ([ "--stdin" , "--no-eslintrc" , "--rule" , "semi:2" ]);
123 child
. stdin
. write ( "var foo = bar // <-- no semicolon \n " );
125 return assertExitCode ( child
, 1 );
129 "gives a detailed error message if no config file is found in /" ,
132 fs
. readdirSync ( "/" ). some (
134 /^\.eslintrc(?:\.(?:js|yaml|yml|json))?$/u
138 return Promise
. resolve ( true );
140 const child
= runESLint (
141 [ "--stdin" ], { cwd
: "/" , env
: { HOME
: "/" } }
144 const exitCodePromise
= assertExitCode ( child
, 2 );
145 const stderrPromise
= getOutput ( child
). then ( output
=> {
148 /ESLint couldn't find a configuration file/u
152 child
. stdin
. write ( "1 < 3; \n " );
154 return Promise
. all ([ exitCodePromise
, stderrPromise
]);
158 it ( "successfully reads from an asynchronous pipe" , () => {
159 const child
= runESLint ([ "--stdin" , "--no-eslintrc" ]);
161 child
. stdin
. write ( "var foo = bar; \n " );
162 return new Promise ( resolve
=> setTimeout ( resolve
, 300 )). then (() => {
163 child
. stdin
. write ( "var baz = qux; \n " );
166 return assertExitCode ( child
, 0 );
170 it ( "successfully handles more than 4k data via stdin" , () => {
171 const child
= runESLint ([ "--stdin" , "--no-eslintrc" ]);
172 const large
= fs
. createReadStream ( path
. join ( __dirname
, "../bench/large.js" ), "utf8" );
174 large
. pipe ( child
. stdin
);
176 return assertExitCode ( child
, 0 );
180 describe ( "running on files" , () => {
181 it ( "has exit code 0 if no linting errors occur" , () => assertExitCode ( runESLint ([ "bin/eslint.js" ]), 0 ));
182 it ( "has exit code 0 if a linting warning is reported" , () => assertExitCode ( runESLint ([ "bin/eslint.js" , "--env" , "es2020" , "--no-eslintrc" , "--rule" , "semi: [1, never]" ]), 0 ));
183 it ( "has exit code 1 if a linting error is reported" , () => assertExitCode ( runESLint ([ "bin/eslint.js" , "--env" , "es2020" , "--no-eslintrc" , "--rule" , "semi: [2, never]" ]), 1 ));
184 it ( "has exit code 1 if a syntax error is thrown" , () => assertExitCode ( runESLint ([ "README.md" ]), 1 ));
187 describe ( "automatically fixing files" , () => {
188 const fixturesPath
= path
. join ( __dirname
, "../fixtures/autofix-integration" );
189 const tempFilePath
= ` ${fixturesPath} /temp.js` ;
190 const startingText
= fs
. readFileSync ( ` ${fixturesPath} /left-pad.js` ). toString ();
191 const expectedFixedText
= fs
. readFileSync ( ` ${fixturesPath} /left-pad-expected.js` ). toString ();
192 const expectedFixedTextQuiet
= fs
. readFileSync ( ` ${fixturesPath} /left-pad-expected-quiet.js` ). toString ();
195 fs
. writeFileSync ( tempFilePath
, startingText
);
198 it ( "has exit code 0 and fixes a file if all rules can be fixed" , () => {
199 const child
= runESLint ([ "--fix" , "--no-eslintrc" , "--no-ignore" , tempFilePath
]);
200 const exitCodeAssertion
= assertExitCode ( child
, 0 );
201 const outputFileAssertion
= awaitExit ( child
). then (() => {
202 assert
. strictEqual ( fs
. readFileSync ( tempFilePath
). toString (), expectedFixedText
);
205 return Promise
. all ([ exitCodeAssertion
, outputFileAssertion
]);
208 it ( "has exit code 0, fixes errors in a file, and does not report or fix warnings if --quiet and --fix are used" , () => {
209 const child
= runESLint ([ "--fix" , "--quiet" , "--no-eslintrc" , "--no-ignore" , tempFilePath
]);
210 const exitCodeAssertion
= assertExitCode ( child
, 0 );
211 const stdoutAssertion
= getOutput ( child
). then ( output
=> assert
. strictEqual ( output
. stdout
, "" ));
212 const outputFileAssertion
= awaitExit ( child
). then (() => {
213 assert
. strictEqual ( fs
. readFileSync ( tempFilePath
). toString (), expectedFixedTextQuiet
);
216 return Promise
. all ([ exitCodeAssertion
, stdoutAssertion
, outputFileAssertion
]);
219 it ( "has exit code 1 and fixes a file if not all rules can be fixed" , () => {
220 const child
= runESLint ([ "--fix" , "--no-eslintrc" , "--no-ignore" , "--rule" , "max-len: [2, 10]" , tempFilePath
]);
221 const exitCodeAssertion
= assertExitCode ( child
, 1 );
222 const outputFileAssertion
= awaitExit ( child
). then (() => {
223 assert
. strictEqual ( fs
. readFileSync ( tempFilePath
). toString (), expectedFixedText
);
226 return Promise
. all ([ exitCodeAssertion
, outputFileAssertion
]);
230 fs
. unlinkSync ( tempFilePath
);
234 describe ( "cache files" , () => {
235 const CACHE_PATH
= ".temp-eslintcache" ;
236 const SOURCE_PATH
= "tests/fixtures/cache/src/test-file.js" ;
237 const ARGS_WITHOUT_CACHE
= [ "--no-eslintrc" , "--no-ignore" , SOURCE_PATH
, "--cache-location" , CACHE_PATH
];
238 const ARGS_WITH_CACHE
= ARGS_WITHOUT_CACHE
. concat ( "--cache" );
240 describe ( "when no cache file exists" , () => {
241 it ( "creates a cache file when the --cache flag is used" , () => {
242 const child
= runESLint ( ARGS_WITH_CACHE
);
244 return assertExitCode ( child
, 0 ). then (() => {
245 assert
. isTrue ( fs
. existsSync ( CACHE_PATH
), "Cache file should exist at the given location" );
247 // Cache file should contain valid JSON
248 JSON
. parse ( fs
. readFileSync ( CACHE_PATH
, "utf8" ));
253 describe ( "when a valid cache file already exists" , () => {
255 const child
= runESLint ( ARGS_WITH_CACHE
);
257 return assertExitCode ( child
, 0 ). then (() => {
258 assert
. isTrue ( fs
. existsSync ( CACHE_PATH
), "Cache file should exist at the given location" );
261 it ( "can lint with an existing cache file and the --cache flag" , () => {
262 const child
= runESLint ( ARGS_WITH_CACHE
);
264 return assertExitCode ( child
, 0 ). then (() => {
266 // Note: This doesn't actually verify that the cache file is used for anything.
267 assert
. isTrue ( fs
. existsSync ( CACHE_PATH
), "Cache file should still exist after linting with --cache" );
270 it ( "updates the cache file when the source file is modified" , () => {
271 const initialCacheContent
= fs
. readFileSync ( CACHE_PATH
, "utf8" );
273 // Update the file to change its mtime
274 fs
. writeFileSync ( SOURCE_PATH
, fs
. readFileSync ( SOURCE_PATH
, "utf8" ));
276 const child
= runESLint ( ARGS_WITH_CACHE
);
278 return assertExitCode ( child
, 0 ). then (() => {
279 const newCacheContent
= fs
. readFileSync ( CACHE_PATH
, "utf8" );
281 assert
. notStrictEqual ( initialCacheContent
, newCacheContent
, "Cache file should change after source is modified" );
284 it ( "deletes the cache file when run without the --cache argument" , () => {
285 const child
= runESLint ( ARGS_WITHOUT_CACHE
);
287 return assertExitCode ( child
, 0 ). then (() => {
288 assert
. isFalse ( fs
. existsSync ( CACHE_PATH
), "Cache file should be deleted after running ESLint without the --cache argument" );
293 // https://github.com/eslint/eslint/issues/7748
294 describe ( "when an invalid cache file already exists" , () => {
296 fs
. writeFileSync ( CACHE_PATH
, "This is not valid JSON." );
300 () => JSON
. parse ( fs
. readFileSync ( CACHE_PATH
, "utf8" )),
303 "Cache file should not contain valid JSON at the start"
307 it ( "overwrites the invalid cache file with a valid one when the --cache argument is used" , () => {
308 const child
= runESLint ( ARGS_WITH_CACHE
);
310 return assertExitCode ( child
, 0 ). then (() => {
311 assert
. isTrue ( fs
. existsSync ( CACHE_PATH
), "Cache file should exist at the given location" );
313 // Cache file should contain valid JSON
314 JSON
. parse ( fs
. readFileSync ( CACHE_PATH
, "utf8" ));
318 it ( "deletes the invalid cache file when the --cache argument is not used" , () => {
319 const child
= runESLint ( ARGS_WITHOUT_CACHE
);
321 return assertExitCode ( child
, 0 ). then (() => {
322 assert
. isFalse ( fs
. existsSync ( CACHE_PATH
), "Cache file should be deleted after running ESLint without the --cache argument" );
328 if ( fs
. existsSync ( CACHE_PATH
)) {
329 fs
. unlinkSync ( CACHE_PATH
);
334 describe ( "handling crashes" , () => {
335 it ( "prints the error message to stderr in the event of a crash" , () => {
336 const child
= runESLint ([ "--rule=no-restricted-syntax:[error, 'Invalid Selector [[[']" , "Makefile.js" ]);
337 const exitCodeAssertion
= assertExitCode ( child
, 2 );
338 const outputAssertion
= getOutput ( child
). then ( output
=> {
339 const expectedSubstring
= "Syntax error in selector" ;
341 assert
. strictEqual ( output
. stdout
, "" );
342 assert
. include ( output
. stderr
, expectedSubstring
);
345 return Promise
. all ([ exitCodeAssertion
, outputAssertion
]);
348 it ( "prints the error message exactly once to stderr in the event of a crash" , () => {
349 const child
= runESLint ([ "--rule=no-restricted-syntax:[error, 'Invalid Selector [[[']" , "Makefile.js" ]);
350 const exitCodeAssertion
= assertExitCode ( child
, 2 );
351 const outputAssertion
= getOutput ( child
). then ( output
=> {
352 const expectedSubstring
= "Syntax error in selector" ;
354 assert
. strictEqual ( output
. stdout
, "" );
355 assert
. include ( output
. stderr
, expectedSubstring
);
357 // The message should appear exactly once in stderr
358 assert
. strictEqual ( output
. stderr
. indexOf ( expectedSubstring
), output
. stderr
. lastIndexOf ( expectedSubstring
));
361 return Promise
. all ([ exitCodeAssertion
, outputAssertion
]);
364 it ( "prints the error message pointing to line of code" , () => {
365 const invalidConfig
= path
. join ( __dirname
, "../fixtures/bin/.eslintrc.yml" );
366 const child
= runESLint ([ "--no-ignore" , invalidConfig
]);
367 const exitCodeAssertion
= assertExitCode ( child
, 2 );
368 const outputAssertion
= getOutput ( child
). then ( output
=> {
369 const expectedSubstring
= ": bad indentation of a mapping entry at line" ;
371 assert
. strictEqual ( output
. stdout
, "" );
372 assert
. include ( output
. stderr
, expectedSubstring
);
375 return Promise
. all ([ exitCodeAssertion
, outputAssertion
]);
380 describe ( "emitting a warning for ecmaFeatures" , () => {
381 it ( "does not emit a warning when it does not find an ecmaFeatures option" , () => {
382 const child
= runESLint ([ "Makefile.js" ]);
384 const exitCodePromise
= assertExitCode ( child
, 0 );
385 const outputPromise
= getOutput ( child
). then ( output
=> assert
. strictEqual ( output
. stderr
, "" ));
387 return Promise
. all ([ exitCodePromise
, outputPromise
]);
389 it ( "emits a warning when it finds an ecmaFeatures option" , () => {
390 const child
= runESLint ([ "-c" , "tests/fixtures/config-file/ecma-features/.eslintrc.yml" , "Makefile.js" ]);
392 const exitCodePromise
= assertExitCode ( child
, 0 );
393 const outputPromise
= getOutput ( child
). then ( output
=> {
394 assert
. include ( output
. stderr
, "The 'ecmaFeatures' config file property is deprecated and has no effect." );
397 return Promise
. all ([ exitCodePromise
, outputPromise
]);
403 // Clean up all the processes after every test.
404 forkedProcesses
. forEach ( child
=> child
. kill ());
405 forkedProcesses
. clear ();