Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce overridable #120

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Expand Up @@ -5,7 +5,7 @@ rewire
[![](https://img.shields.io/npm/v/rewire.svg)](https://www.npmjs.com/package/rewire)
[![](https://img.shields.io/npm/dm/rewire.svg)](https://www.npmjs.com/package/rewire)
[![Dependency Status](https://david-dm.org/jhnns/rewire.svg)](https://david-dm.org/jhnns/rewire)
[![Build Status](https://travis-ci.org/jhnns/rewire.svg?branch=master)](https://travis-ci.org/rewire/jhnns)
[![Build Status](https://travis-ci.org/jhnns/rewire.svg?branch=master)](https://travis-ci.org/jhnns/rewire)
[![Coverage Status](https://img.shields.io/coveralls/jhnns/rewire.svg)](https://coveralls.io/r/jhnns/rewire?branch=master)

rewire adds a special setter and getter to modules so you can modify their behaviour for better unit testing. You may
Expand Down Expand Up @@ -88,6 +88,10 @@ myModule.__set__({
You may also override globals. These changes are only within the module, so you don't have to be concerned that other modules are influenced by your mock.

```javascript
var options = {
overridable: ["console", "process"]
};
var myModule = rewire("../lib/myModule.js", options);
myModule.__set__({
console: {
log: function () { /* be quiet */ }
Expand Down
18 changes: 9 additions & 9 deletions lib/getImportGlobalsSrc.js
Expand Up @@ -7,32 +7,32 @@
*
* @return {String}
*/
function getImportGlobalsSrc(ignore) {
function getImportGlobalsSrc(overridable) {
var key,
value,
src = "",
ignore = [],
globalObj = typeof global === "undefined"? window: global;

ignore = ignore || [];
// global itself can't be overridden because it's the only reference to our real global objects
ignore.push("global");
// ignore 'module', 'exports' and 'require' on the global scope, because otherwise our code would
// shadow the module-internal variables
// @see https://github.com/jhnns/rewire-webpack/pull/6
ignore.push("module", "exports", "require");

for (key in globalObj) { /* jshint forin: false */
(overridable || []).forEach(function forEachKey(key) {
if (ignore.indexOf(key) !== -1) {
continue;
return;
}
value = globalObj[key];

// key may be an invalid variable name (e.g. 'a-b')
try {
eval("var " + key + ";");
src += "var " + key + " = global." + key + "; ";
// jshint -W061
eval("var " + key + ";");
// jshint +W061
src += "var " + key + " = global." + key + "; ";
} catch(e) {}
}
});

return src;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/index.js
Expand Up @@ -5,10 +5,11 @@ var rewireModule = require("./rewire.js");
* call myModule.__set__(name, value) and myModule.__get__(name) to manipulate private variables.
*
* @param {!String} filename Path to the module that shall be rewired. Use it exactly like require().
* @param {Object} options Options.
* @return {*} the rewired module
*/
function rewire(filename) {
return rewireModule(module.parent, filename);
function rewire(filename, options) {
return rewireModule(module.parent, filename, options);
}

module.exports = rewire;
Expand Down
6 changes: 4 additions & 2 deletions lib/rewire.js
Expand Up @@ -9,7 +9,7 @@ var Module = require("module"),
/**
* Does actual rewiring the module. For further documentation @see index.js
*/
function internalRewire(parentModulePath, targetPath) {
function internalRewire(parentModulePath, targetPath, options) {
var targetModule,
prelude,
appendix,
Expand All @@ -20,14 +20,16 @@ function internalRewire(parentModulePath, targetPath) {
throw new TypeError("Filename must be a string");
}

options = options || {};

// Resolve full filename relative to the parent module
targetPath = Module._resolveFilename(targetPath, parentModulePath);

// Create testModule as it would be created by require()
targetModule = new Module(targetPath, parentModulePath);

// We prepend a list of all globals declared with var so they can be overridden (without changing original globals)
prelude = getImportGlobalsSrc();
prelude = getImportGlobalsSrc(options.overridable);

// Wrap module src inside IIFE so that function declarations do not clash with global variables
// @see https://github.com/jhnns/rewire/issues/56
Expand Down
59 changes: 44 additions & 15 deletions test/getImportGlobalsSrc.test.js
Expand Up @@ -3,7 +3,7 @@ var expect = require("expect.js"),
getImportGlobalsSrc = require("../lib/getImportGlobalsSrc.js");

describe("getImportGlobalsSrc", function () {
it("should declare all globals with a var", function () {
it("should declare no globals with a var", function () {
var context = {
global: global
},
Expand All @@ -18,44 +18,73 @@ describe("getImportGlobalsSrc", function () {
global.require = require;

// Also make sure it ignores invalid variable names
global['a-b'] = true;
global["a-b"] = true;

src = getImportGlobalsSrc();

delete global.module;
delete global.exports;
delete global.require;
delete global['__core-js_shared__'];
delete global['a-b'];
delete global["__core-js_shared__"];
delete global["a-b"];

expectedGlobals = Object.keys(global);
expectedGlobals = ["global"];

vm.runInNewContext(src, context);
actualGlobals = Object.keys(context);
actualGlobals.sort();
expectedGlobals.sort();
expect(actualGlobals).to.eql(expectedGlobals);
expect(actualGlobals.length).to.be.above(1);
});
it("should ignore the given variables", function () {
it("should declare overridable globals with a var", function () {
var context = {
global: global
},
ignore = ["console", "setTimeout"],
expectedGlobals,
overridable = ["clearTimeout", "setTimeout"],
src,
actualGlobals;

// Temporarily set module-internal variables on the global scope to check if getImportGlobalsSrc()
// ignores them properly
global.module = module;
global.exports = exports;
global.require = require;

// Also make sure it ignores invalid variable names
global["a-b"] = true;

src = getImportGlobalsSrc(overridable);

delete global.module;
delete global.exports;
delete global.require;
delete global["__core-js_shared__"];
delete global["a-b"];

expectedGlobals = ["clearTimeout", "global", "setTimeout"];

vm.runInNewContext(src, context);
actualGlobals = Object.keys(context);
actualGlobals.sort();
expectedGlobals.sort();
expect(actualGlobals).to.eql(expectedGlobals);
});
it("should ignore module-internal variables variables", function () {
var context = {
global: global
},
overridable = ["module", "exports", "require"],
src,
actualGlobals,
expectedGlobals = Object.keys(global);
expectedGlobals = ["global"];

src = getImportGlobalsSrc(overridable);

// getImportGlobalsSrc modifies the ignore array, so let's create a copy
src = getImportGlobalsSrc(ignore.slice(0));
expectedGlobals = expectedGlobals.filter(function filterIgnoredVars(value) {
return ignore.indexOf(value) === -1;
});
vm.runInNewContext(src, context);
actualGlobals = Object.keys(context);
actualGlobals.sort();
expectedGlobals.sort();
expect(actualGlobals).to.eql(expectedGlobals);
expect(actualGlobals.length).to.be.above(1);
});
});
15 changes: 11 additions & 4 deletions testLib/sharedTestCases.js
Expand Up @@ -161,8 +161,11 @@ module.exports = function () {
});

it("should provide the ability to mock global objects just within the module", function () {
var rewiredModuleA = rewire("./moduleA.js"),
rewiredModuleB = rewire("./moduleB.js"),
var options = {
overridable: ["console", "__filename", "Buffer", "document"]
},
rewiredModuleA = rewire("./moduleA.js", options),
rewiredModuleB = rewire("./moduleB.js", options),
consoleMock = {},
bufferMock = {},
documentMock = {},
Expand Down Expand Up @@ -196,14 +199,18 @@ module.exports = function () {

if (typeof window === "undefined") {
global.someGlobalVar = "test";
rewiredModule = rewire("./moduleA.js");
rewiredModule = rewire("./moduleA.js", {
overridable: ["someGlobalVar"]
});
rewiredModule.__set__("someGlobalVar", "other value");
expect(global.someGlobalVar).to.be("test");
expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
delete global.someGlobalVar;
} else {
window.someGlobalVar = "test";
rewiredModule = rewire("./moduleA.js");
rewiredModule = rewire("./moduleA.js", {
overridable: ["someGlobalVar"]
});
rewiredModule.__set__("someGlobalVar", "other value");
expect(window.someGlobalVar).to.be("test");
expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
Expand Down