Skip to content
4 changes: 1 addition & 3 deletions bin/_mocha
Original file line number Diff line number Diff line change
Expand Up @@ -591,9 +591,7 @@ if (program.watch) {
};

purge = () => {
watchFiles.forEach(file => {
delete require.cache[file];
});
watchFiles.forEach(mocha.unloadFile);
};

loadAndRun();
Expand Down
38 changes: 37 additions & 1 deletion lib/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,8 @@ Mocha.prototype.ui = function(name) {
*
* @private
* @see {@link Mocha#addFile}
* @see {@link Mocha#run}
* @see {@link Mocha#unloadFiles}
* @param {Function} [fn] - Callback invoked upon completion.
*/
Mocha.prototype.loadFiles = function(fn) {
Expand All @@ -290,6 +292,39 @@ Mocha.prototype.loadFiles = function(fn) {
fn && fn();
};

/**
* Removes a previously loaded file from Node's `require` cache.
*
* @private
* @see {@link Mocha#unloadFiles}
* @param {string} file - Pathname of file to be unloaded.
*/
Mocha.prototype.unloadFile = function(file) {
delete require.cache[require.resolve(file)];
};

/**
* @summary
* Unloads `files` from Node's `require` cache.
*
* @description
* This allows files to be "freshly" reloaded, providing the ability
* to reuse a Mocha instance programmatically.
*
* <strong>Not needed for (or used by) CLI.</strong>
*
* @public
* @see {@link Mocha#unloadFile}
* @see {@link Mocha#loadFiles}
* @see {@link Mocha#run}
* @returns {Mocha} this
* @chainable
*/
Mocha.prototype.unloadFiles = function() {
this.files.forEach(this.unloadFile, this);
return this;
};

/**
* Implements desktop notifications using a pseudo-reporter.
*
Expand Down Expand Up @@ -686,7 +721,7 @@ Mocha.prototype.forbidPending = function() {

/**
* @summary
* Runs tests and invokes `fn()` when complete.
* Runs the root suite and invokes `fn()` when complete.
*
* @description
* To run tests multiple times (or to run tests in files that are
Expand All @@ -695,6 +730,7 @@ Mocha.prototype.forbidPending = function() {
*
* @public
* @see {@link Mocha#loadFiles}
* @see {@link Mocha#unloadFiles}
* @see {@link Runner#run}
* @param {DoneCB} [fn] - Callback invoked when test execution completed.
* @return {Runner} runner instance
Expand Down
40 changes: 40 additions & 0 deletions test/node-unit/mocha.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

var Mocha = require('../../lib/mocha');
var Path = require('path');

describe('.unloadFile()', function() {
it('should unload a specific file from cache', function() {
var mocha = new Mocha({});
var resolvedFilePath;
var filePath = __filename;

mocha.addFile(filePath);
mocha.loadFiles();
resolvedFilePath = require.resolve(filePath);
expect(require.cache, 'to have key', resolvedFilePath);

mocha.unloadFile(filePath);
expect(require.cache, 'not to have key', resolvedFilePath);
});
});

describe('.unloadFiles()', function() {
it('should unload all test files from cache', function() {
var mocha = new Mocha({});
var resolvedTestFiles;
var testFiles = [
__filename,
Path.join(__dirname, 'fs.spec.js'),
Path.join(__dirname, 'color.spec.js')
];

testFiles.forEach(mocha.addFile, mocha);
mocha.loadFiles();
resolvedTestFiles = testFiles.map(require.resolve);
expect(require.cache, 'to have keys', resolvedTestFiles);

mocha.unloadFiles();
expect(require.cache, 'not to have keys', resolvedTestFiles);
});
});