UNPKG

rewiremock

Version:

Advanced dependency mocking device.

344 lines (297 loc) 8.5 kB
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 };