rewiremock
Version:
Advanced dependency mocking device.
344 lines (297 loc) • 8.5 kB
JavaScript
var _this = this;
import Module, { getModuleName, getModuleParent } from './module';
import wipeCache, { safelyRemoveCache } from './wipeCache';
import createScope from './scope';
import { getScopeVariable, setScope } from './globals';
import { convertName, onMockCreate, onDisable, onEnable, addPlugin as addPluginAPI, removePlugin as removePluginAPI } from './plugins';
import { resetMock, getMock, getAsyncMock, getAsyncModuleName, getAllMocks } from './mocks';
import ModuleMock from './mock';
var parentModule = getModuleParent(module);
var mockScope = null;
var scope = function scope() {
return setScope(mockScope);
};
var updateScope = function updateScope() {
var parentScope = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
mockScope = createScope(parentScope, parentModule);
scope();
};
updateScope();
/** main **/
/**
* @name rewiremock
* @param {String|Function} module name
* @return {ModuleMock}
*/
function mockModule(moduleName) {
scope();
if (typeof moduleName === 'function') {
return onMockCreate(new ModuleMock(getAsyncMock(moduleName, parentModule)));
} else {
var name = convertName(moduleName, parentModule);
resetMock(name);
return onMockCreate(new ModuleMock(getMock(name)));
}
}
mockModule.getMock = function (module) {
var moduleName = module;
if (typeof moduleName === 'function') {
moduleName = getAsyncModuleName(moduleName, parentModule);
} else {
moduleName = convertName(moduleName, parentModule);
}
var mock = getMock(moduleName);
if (mock) {
return new ModuleMock(mock);
}
return null;
};
/**
* @name rewiremock.resolve
* @param {String} module name
* @return {String} converted module name
*/
mockModule.resolve = function (module) {
scope();
return convertName(module, parentModule);
};
/** flags **/
/**
* Activates module isolation
* @param {Object} [options]
* @param {Boolean} [options.noAutoPassBy] excludes mocked modules to a isolation scope. Use it with mock.callThrough.
* @param {Boolean} [options.noParentPassBy] disable allowing any module, with allowed parent
*/
mockModule.isolation = function () {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
mockScope.isolation = Object.assign({}, options);
return mockModule;
};
/**
* Deactivates isolation
*/
mockModule.withoutIsolation = function () {
mockScope.isolation = false;
return mockModule;
};
mockModule.forceCacheClear = function (mode) {
mockScope.forceCacheClear = mode !== undefined ? mode : true;
};
mockModule.setCacheControl = function (mode) {
mockScope.cacheControl = mode;
};
/**
* Adding new passby record
* @param {String|RegEx|Function} pattern
*/
mockModule.passBy = function (pattern) {
mockScope.passBy.push(pattern);
return mockModule;
};
mockModule.overrideEntryPoint = function (parent) {
mockScope.parentModule = parentModule = parent || getModuleParent(getModuleParent(module));
};
/** interface **/
/**
* enabled rewiremock
*/
mockModule.enable = function () {
scope();
Module.probeSyncModules();
Module.overloadRequire();
storeCache();
if (getScopeVariable('cacheControl') !== false) {
wipeCache();
}
onEnable(getAllMocks());
return mockModule;
};
/**
* disabled rewiremock
*/
mockModule.disable = function () {
scope();
Module.restoreRequire();
onDisable(getAllMocks());
mockModule.withoutIsolation();
mockModule.flush();
return mockModule;
};
/**
* Requires file with hooks
* @param {String|Function} file
* @param {Object|Function} overrides
*/
mockModule.proxy = function (file) {
var overrides = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var result = 0;
mockModule.inScope(function () {
var stubs = (typeof overrides === 'function' ? overrides(ModuleMock.inlineConstructor) : overrides) || {};
Object.keys(stubs).forEach(function (key) {
return mockModule(key).from(stubs[key]);
});
mockModule.enable();
if (typeof file === 'string') {
result = mockModule.requireActual(file);
} else {
result = file();
}
mockModule.disable();
});
return result;
};
/**
* Imports file with hooks
* @param {Function} importFunction (use import)
* @param {Object|Function} overrides
* @return {Promise}
*/
mockModule.module = function (importFunction) {
var overrides = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
return mockModule.around(importFunction, function () {
var stubs = (typeof overrides === 'function' ? overrides(ModuleMock.inlineConstructor) : overrides) || {};
Object.keys(stubs).forEach(function (key) {
return mockModule(key).from(stubs[key]);
});
});
};
/**
* Creates temporary executing scope. All mocks and plugins you will add in callback will be removed at exit.
* @param callback
*/
mockModule.inScope = function (callback) {
var currentScope = mockScope;
var error = void 0;
updateScope(currentScope);
try {
callback();
} catch (e) {
error = e;
}
mockScope = currentScope;
if (error) throw error;
return mockModule;
};
/**
* executes module in sandbox
* @param {Function} loader loader callback
* @param {Function} [createCallback] - optional callback to be executed before load.
* @return {Promise}
*/
mockModule.around = function (loader, createCallback) {
return new Promise(function (resolve, reject) {
var currentScope = mockScope;
updateScope(currentScope);
var restore = function restore() {
mockModule.disable();
mockScope = currentScope;
};
Promise.resolve(createCallback && createCallback(mockModule)).then(function () {
return Module.probeAsyncModules();
}).then(function () {
return mockModule.enable();
}).then(function () {
return Promise.resolve(loader()).then(function (mockedResult) {
restore();
resolve(mockedResult);
}, function (err) {
restore();
reject(err);
});
});
});
};
mockModule.stubFactory = function (factory) {
var currentScope = mockScope;
currentScope.options.stubFactory = factory;
};
var storeCache = function storeCache() {
mockScope.requireCache = mockScope.requireCache || Object.assign({}, require.cache);
};
var restoreCache = function restoreCache() {
var oldCache = mockScope.requireCache;
var newCache = require.cache;
if (oldCache) {
Object.keys(oldCache)
//.filter(key => !newCache[key])
.forEach(function (key) {
return newCache[key] = oldCache[key];
});
mockScope.requireCache = null;
}
};
var swapCache = function swapCache() {
var oldCache = mockScope.requireCache;
var newCache = require.cache;
if (oldCache) {
Object.keys(newCache).filter(function (key) {
return !oldCache[key];
}).filter(function (key) {
return key.indexOf('.node') < 0;
}).forEach(function (key) {
return delete newCache[key];
});
Object.keys(oldCache).forEach(function (key) {
return newCache[key] = oldCache[key];
});
mockScope.requireCache = null;
}
};
/**
* flushes all active overrides
*/
mockModule.flush = function () {
var forceCacheClear = getScopeVariable('forceCacheClear');
// flush away soiled modules
wipeCache(mockScope.mockedModules);
mockScope.mockedModules = {};
if (forceCacheClear) {
if (forceCacheClear !== 'nocache') {
// restore cache completely
swapCache();
}
} else {
// merge caches
restoreCache();
}
};
/**
* Low-level require
* @param {String} fileName
*/
mockModule.requireActual = function (fileName) {
return Module.require(Module.relativeFileName(fileName, parentModule), parentModule);
};
/**
* Low-level import
* @param {String} fileName
*/
mockModule.importActual = function (fileName) {
return Promise.resolve(_this.requireActual(fileName));
};
/**
* flushes anything
*/
mockModule.clear = function () {
updateScope();
scope();
mockModule.withoutIsolation();
mockModule.flush();
};
var cleanup = function cleanup() {
safelyRemoveCache(getModuleName(module));
};
var addPlugin = function addPlugin(plugin) {
plugin.init();
scope();
addPluginAPI(plugin);
};
var removePlugin = function removePlugin(plugin) {
scope();
removePluginAPI(plugin);
};
mockModule.addPlugin = function (plugin) {
addPlugin(plugin);
return mockModule;
};
export { mockModule, addPlugin, removePlugin, cleanup };