123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- 'use strict';
-
- const path = require('path');
- const chokidar = require('chokidar');
- const Context = require('../context');
- const collectFiles = require('./collect-files');
-
- /**
- * Exports the `watchRun` function that runs mocha in "watch" mode.
- * @see module:lib/cli/run-helpers
- * @module
- * @private
- */
-
- /**
- * Run Mocha in "watch" mode
- * @param {Mocha} mocha - Mocha instance
- * @param {Object} opts - Options
- * @param {string[]} [opts.watchFiles] - List of paths and patterns to
- * watch. If not provided all files with an extension included in
- * `fileColletionParams.extension` are watched. See first argument of
- * `chokidar.watch`.
- * @param {string[]} opts.watchIgnore - List of paths and patterns to
- * exclude from watching. See `ignored` option of `chokidar`.
- * @param {Object} fileCollectParams - Parameters that control test
- * file collection. See `lib/cli/collect-files.js`.
- * @param {string[]} fileCollectParams.extension - List of extensions
- * to watch if `opts.watchFiles` is not given.
- * @private
- */
- module.exports = (mocha, {watchFiles, watchIgnore}, fileCollectParams) => {
- if (!watchFiles) {
- watchFiles = fileCollectParams.extension.map(ext => `**/*.${ext}`);
- }
-
- const watcher = chokidar.watch(watchFiles, {
- ignored: watchIgnore,
- ignoreInitial: true
- });
-
- const rerunner = createRerunner(mocha, () => {
- getWatchedFiles(watcher).forEach(file => {
- delete require.cache[file];
- });
- mocha.files = collectFiles(fileCollectParams);
- });
-
- watcher.on('ready', () => {
- rerunner.run();
- });
-
- watcher.on('all', () => {
- rerunner.scheduleRun();
- });
-
- console.log();
- hideCursor();
- process.on('exit', () => {
- showCursor();
- });
- process.on('SIGINT', () => {
- showCursor();
- console.log('\n');
- process.exit(128 + 2);
- });
-
- // Keyboard shortcut for restarting when "rs\n" is typed (ala Nodemon)
- process.stdin.resume();
- process.stdin.setEncoding('utf8');
- process.stdin.on('data', data => {
- const str = data
- .toString()
- .trim()
- .toLowerCase();
- if (str === 'rs') rerunner.scheduleRun();
- });
- };
-
- /**
- * Create an object that allows you to rerun tests on the mocha
- * instance. `beforeRun` is called everytime before `mocha.run()` is
- * called.
- *
- * @param {Mocha} mocha - Mocha instance
- * @param {function} beforeRun - Called just before `mocha.run()`
- */
- const createRerunner = (mocha, beforeRun) => {
- // Set to a `Runner` when mocha is running. Set to `null` when mocha is not
- // running.
- let runner = null;
-
- let rerunScheduled = false;
-
- const run = () => {
- try {
- beforeRun();
- resetMocha(mocha);
- runner = mocha.run(() => {
- runner = null;
- if (rerunScheduled) {
- rerun();
- }
- });
- } catch (e) {
- console.log(e.stack);
- }
- };
-
- const scheduleRun = () => {
- if (rerunScheduled) {
- return;
- }
-
- rerunScheduled = true;
- if (runner) {
- runner.abort();
- } else {
- rerun();
- }
- };
-
- const rerun = () => {
- rerunScheduled = false;
- eraseLine();
- run();
- };
-
- return {
- scheduleRun,
- run
- };
- };
-
- /**
- * Return the list of absolute paths watched by a chokidar watcher.
- *
- * @param watcher - Instance of a chokidar watcher
- * @return {string[]} - List of absolute paths
- */
- const getWatchedFiles = watcher => {
- const watchedDirs = watcher.getWatched();
- let watchedFiles = [];
- Object.keys(watchedDirs).forEach(dir => {
- watchedFiles = watchedFiles.concat(
- watchedDirs[dir].map(file => path.join(dir, file))
- );
- });
- return watchedFiles;
- };
-
- /**
- * Reset the internal state of the mocha instance so that tests can be rerun.
- *
- * @param {Mocha} mocha - Mocha instance
- * @private
- */
- const resetMocha = mocha => {
- mocha.unloadFiles();
- mocha.suite = mocha.suite.clone();
- mocha.suite.ctx = new Context();
- // Registers a callback on `mocha.suite` that wires new context to the DSL
- // (e.g. `describe`) that is exposed as globals when the test files are
- // reloaded.
- mocha.ui(mocha.options.ui);
- };
-
- /**
- * Hide the cursor.
- * @ignore
- * @private
- */
- const hideCursor = () => {
- process.stdout.write('\u001b[?25l');
- };
-
- /**
- * Show the cursor.
- * @ignore
- * @private
- */
- const showCursor = () => {
- process.stdout.write('\u001b[?25h');
- };
-
- /**
- * Erases the line on stdout
- * @private
- */
- const eraseLine = () => {
- process.stdout.write('\u001b[2K');
- };
|