diff --git a/lib/cli/collect-files.js b/lib/cli/collect-files.js new file mode 100644 index 0000000000..ff35ed3d36 --- /dev/null +++ b/lib/cli/collect-files.js @@ -0,0 +1,84 @@ +'use strict'; + +const path = require('path'); +const ansi = require('ansi-colors'); +const debug = require('debug')('mocha:cli:run:helpers'); +const minimatch = require('minimatch'); +const utils = require('../utils'); + +/** + * Exports a function that collects test files from CLI parameters. + * @see module:lib/cli/run-helpers + * @see module:lib/cli/watch-run + * @module + * @private + */ + +/** + * Smash together an array of test files in the correct order + * @param {Object} opts - Options + * @param {string[]} opts.extension - File extensions to use + * @param {string[]} opts.spec - Files, dirs, globs to run + * @param {string[]} opts.ignore - Files, dirs, globs to ignore + * @param {string[]} opts.file - List of additional files to include + * @param {boolean} opts.recursive - Find files recursively + * @param {boolean} opts.sort - Sort test files + * @returns {string[]} List of files to test + * @private + */ +module.exports = ({ignore, extension, file, recursive, sort, spec} = {}) => { + let files = []; + const unmatched = []; + spec.forEach(arg => { + let newFiles; + try { + newFiles = utils.lookupFiles(arg, extension, recursive); + } catch (err) { + if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') { + unmatched.push({message: err.message, pattern: err.pattern}); + return; + } + + throw err; + } + + if (typeof newFiles !== 'undefined') { + if (typeof newFiles === 'string') { + newFiles = [newFiles]; + } + newFiles = newFiles.filter(fileName => + ignore.every(pattern => !minimatch(fileName, pattern)) + ); + } + + files = files.concat(newFiles); + }); + + if (!files.length) { + // give full message details when only 1 file is missing + const noneFoundMsg = + unmatched.length === 1 + ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw + : 'Error: No test files found'; + console.error(ansi.red(noneFoundMsg)); + process.exit(1); + } else { + // print messages as an warning + unmatched.forEach(warning => { + console.warn(ansi.yellow(`Warning: ${warning.message}`)); + }); + } + + const fileArgs = file.map(filepath => path.resolve(filepath)); + files = files.map(filepath => path.resolve(filepath)); + + // ensure we don't sort the stuff from fileArgs; order is important! + if (sort) { + files.sort(); + } + + // add files given through --file to be ran first + files = fileArgs.concat(files); + debug('files (in order): ', files); + return files; +}; diff --git a/lib/cli/run-helpers.js b/lib/cli/run-helpers.js index a3848598f9..fce5e6250c 100644 --- a/lib/cli/run-helpers.js +++ b/lib/cli/run-helpers.js @@ -9,11 +9,9 @@ const fs = require('fs'); const path = require('path'); -const ansi = require('ansi-colors'); const debug = require('debug')('mocha:cli:run:helpers'); -const minimatch = require('minimatch'); -const utils = require('../utils'); const watchRun = require('./watch-run'); +const collectFiles = require('./collect-files'); const cwd = (exports.cwd = process.cwd()); @@ -94,90 +92,18 @@ exports.handleRequires = (requires = []) => { }; /** - * Smash together an array of test files in the correct order - * @param {Object} [opts] - Options - * @param {string[]} [opts.extension] - File extensions to use - * @param {string[]} [opts.spec] - Files, dirs, globs to run - * @param {string[]} [opts.ignore] - Files, dirs, globs to ignore - * @param {boolean} [opts.recursive=false] - Find files recursively - * @param {boolean} [opts.sort=false] - Sort test files - * @returns {string[]} List of files to test - * @private - */ -exports.handleFiles = ({ - ignore = [], - extension = [], - file = [], - recursive = false, - sort = false, - spec = [] -} = {}) => { - let files = []; - const unmatched = []; - spec.forEach(arg => { - let newFiles; - try { - newFiles = utils.lookupFiles(arg, extension, recursive); - } catch (err) { - if (err.code === 'ERR_MOCHA_NO_FILES_MATCH_PATTERN') { - unmatched.push({message: err.message, pattern: err.pattern}); - return; - } - - throw err; - } - - if (typeof newFiles !== 'undefined') { - if (typeof newFiles === 'string') { - newFiles = [newFiles]; - } - newFiles = newFiles.filter(fileName => - ignore.every(pattern => !minimatch(fileName, pattern)) - ); - } - - files = files.concat(newFiles); - }); - - if (!files.length) { - // give full message details when only 1 file is missing - const noneFoundMsg = - unmatched.length === 1 - ? `Error: No test files found: ${JSON.stringify(unmatched[0].pattern)}` // stringify to print escaped characters raw - : 'Error: No test files found'; - console.error(ansi.red(noneFoundMsg)); - process.exit(1); - } else { - // print messages as an warning - unmatched.forEach(warning => { - console.warn(ansi.yellow(`Warning: ${warning.message}`)); - }); - } - - const fileArgs = file.map(filepath => path.resolve(filepath)); - files = files.map(filepath => path.resolve(filepath)); - - // ensure we don't sort the stuff from fileArgs; order is important! - if (sort) { - files.sort(); - } - - // add files given through --file to be ran first - files = fileArgs.concat(files); - debug('files (in order): ', files); - return files; -}; - -/** - * Give Mocha files and tell it to run + * Collect test files and run mocha instance. * @param {Mocha} mocha - Mocha instance - * @param {Options} [opts] - Options - * @param {string[]} [opts.files] - List of test files + * @param {Options} [opts] - Command line options * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete + * @param {Object} fileCollectParams - Parameters that control test + * file collection. See `lib/cli/collect-files.js`. * @returns {Runner} * @private */ -exports.singleRun = (mocha, {files = [], exit = false} = {}) => { +exports.singleRun = (mocha, {exit}, fileCollectParams) => { + const files = collectFiles(fileCollectParams); + debug('running tests with files', files); mocha.files = files; return mocha.run(exit ? exitMocha : exitMochaLater); }; @@ -185,24 +111,36 @@ exports.singleRun = (mocha, {files = [], exit = false} = {}) => { /** * Actually run tests * @param {Mocha} mocha - Mocha instance - * @param {Object} [opts] - Options - * @param {boolean} [opts.watch=false] - Enable watch mode - * @param {string[]} [opts.extension] - List of extensions to watch - * @param {string|RegExp} [opts.grep] - Grep for test titles - * @param {string} [opts.ui=bdd] - User interface - * @param {boolean} [opts.exit=false] - Force-exit Mocha when tests done - * @param {string[]} [files] - Array of test files + * @param {Object} opts - Command line options * @private */ -exports.runMocha = ( - mocha, - {watch = false, extension = [], grep = '', ui = 'bdd', exit = false} = {}, - files = [] -) => { +exports.runMocha = (mocha, options) => { + const { + watch = false, + extension = [], + grep = '', + ui = 'bdd', + exit = false, + ignore = [], + file = [], + recursive = false, + sort = false, + spec = [] + } = options; + + const fileCollectParams = { + ignore, + extension, + file, + recursive, + sort, + spec + }; + if (watch) { - watchRun(mocha, {extension, grep, ui, files}); + watchRun(mocha, {ui, grep}, fileCollectParams); } else { - exports.singleRun(mocha, {files, exit}); + exports.singleRun(mocha, {exit}, fileCollectParams); } }; diff --git a/lib/cli/run.js b/lib/cli/run.js index da5ffd7b6a..bb7c021998 100644 --- a/lib/cli/run.js +++ b/lib/cli/run.js @@ -16,7 +16,6 @@ const { const { list, - handleFiles, handleRequires, validatePlugin, runMocha @@ -290,8 +289,5 @@ exports.builder = yargs => exports.handler = argv => { debug('post-yargs config', argv); const mocha = new Mocha(argv); - const files = handleFiles(argv); - - debug('running tests with files', files); - runMocha(mocha, argv, files); + runMocha(mocha, argv); }; diff --git a/lib/cli/watch-run.js b/lib/cli/watch-run.js index 54765b7cf7..10d4407673 100644 --- a/lib/cli/watch-run.js +++ b/lib/cli/watch-run.js @@ -3,6 +3,7 @@ const utils = require('../utils'); const Context = require('../context'); const Mocha = require('../mocha'); +const collectFiles = require('./collect-files'); /** * Exports the `watchRun` function that runs mocha in "watch" mode. @@ -15,14 +16,16 @@ const Mocha = require('../mocha'); * Run Mocha in "watch" mode * @param {Mocha} mocha - Mocha instance * @param {Object} opts - Options - * @param {string[]} opts.extension - List of extensions to watch * @param {string|RegExp} opts.grep - Grep for test titles * @param {string} opts.ui - User interface - * @param {string[]} opts.files - Array of test files + * @param {Object} fileCollectParams - Parameters that control test + * file collection. See `lib/cli/collect-files.js`. + * @param {string[]} fileCollectParams.extension - List of extensions to watch * @private */ -module.exports = (mocha, {extension, grep, ui, files}) => { +module.exports = (mocha, {grep, ui}, fileCollectParams) => { let runner; + const files = collectFiles(fileCollectParams); console.log(); hideCursor(); @@ -34,7 +37,7 @@ module.exports = (mocha, {extension, grep, ui, files}) => { process.exit(128 + 2); }); - const watchFiles = utils.files(process.cwd(), extension); + const watchFiles = utils.files(process.cwd(), fileCollectParams.extension); let runAgain = false; const loadAndRun = () => {