From a7bdea28cca4bf37ad43e4948997b0af48822d6a Mon Sep 17 00:00:00 2001 From: Johannes Ewald Date: Sun, 15 Nov 2015 22:02:50 +0100 Subject: Move testLib into dedicated folder inside root folder --- testLib/debuggerModule.js | 7 + testLib/emptyModule.js | 5 + testLib/implicitGlobal.js | 6 + testLib/module.coffee | 3 + testLib/moduleA.js | 135 ++++++++++++ testLib/moduleB.js | 137 ++++++++++++ testLib/node_modules/rewire/package.json | 3 + testLib/sharedTestCases.js | 357 +++++++++++++++++++++++++++++++ testLib/someOtherModule.js | 6 + testLib/strictModule.js | 7 + testLib/throwError.js | 3 + 11 files changed, 669 insertions(+) create mode 100644 testLib/debuggerModule.js create mode 100644 testLib/emptyModule.js create mode 100644 testLib/implicitGlobal.js create mode 100644 testLib/module.coffee create mode 100644 testLib/moduleA.js create mode 100644 testLib/moduleB.js create mode 100644 testLib/node_modules/rewire/package.json create mode 100644 testLib/sharedTestCases.js create mode 100644 testLib/someOtherModule.js create mode 100644 testLib/strictModule.js create mode 100644 testLib/throwError.js (limited to 'testLib') diff --git a/testLib/debuggerModule.js b/testLib/debuggerModule.js new file mode 100644 index 0000000..9d2b7df --- /dev/null +++ b/testLib/debuggerModule.js @@ -0,0 +1,7 @@ +"use strict"; // run code in ES5 strict mode + +var myNumber = 0; + +module.exports = function () { + myNumber = 1; +}; \ No newline at end of file diff --git a/testLib/emptyModule.js b/testLib/emptyModule.js new file mode 100644 index 0000000..8b82a74 --- /dev/null +++ b/testLib/emptyModule.js @@ -0,0 +1,5 @@ +"use strict"; // run code in ES5 strict mode + +var someVar; + +// Comment on file end. Hope this won't break anything \ No newline at end of file diff --git a/testLib/implicitGlobal.js b/testLib/implicitGlobal.js new file mode 100644 index 0000000..3048742 --- /dev/null +++ b/testLib/implicitGlobal.js @@ -0,0 +1,6 @@ +implicitGlobal = "this is an implicit global var ..." + + "yes, it's bad coding style but there are still some libs out there"; + +module.exports = function () { + return undefinedImplicitGlobal; +}; diff --git a/testLib/module.coffee b/testLib/module.coffee new file mode 100644 index 0000000..5487878 --- /dev/null +++ b/testLib/module.coffee @@ -0,0 +1,3 @@ +fs = require "fs" + +exports.readFileSync = () -> fs.readFileSync() \ No newline at end of file diff --git a/testLib/moduleA.js b/testLib/moduleA.js new file mode 100644 index 0000000..b9dce87 --- /dev/null +++ b/testLib/moduleA.js @@ -0,0 +1,135 @@ +"use strict"; // run code in ES5 strict mode + +var someOtherModule = require("./someOtherModule.js"), + myNumber = 0, // copy by value + myObj = {}, // copy by reference + env = "bla", + fs; + +// We need getters and setters for private vars to check if our injected setters and getters actual work +function setMyNumber(newNumber) { + myNumber = newNumber; +} + +function getMyNumber() { + return myNumber; +} + +function setMyObj(newObj) { + myObj = newObj; +} + +function getMyObj() { + return myObj; +} + +function readFileSync() { + fs.readFileSync("bla.txt", "utf8"); +} + +function checkSomeGlobals() { + var isLowerIE, + typeOfGlobalFunc; + + if (typeof navigator !== "undefined") { + isLowerIE = /MSIE [6-8]\.[0-9]/g.test(navigator.userAgent); + } + if (isLowerIE) { + typeOfGlobalFunc = "object"; + } else { + typeOfGlobalFunc = "function"; + } + + if (typeof global !== "object") { + throw new ReferenceError("global is not an object"); + } + if (typeof console !== "object") { + throw new ReferenceError("console is not an object"); + } + if (typeof require !== "function") { + throw new ReferenceError("require is not a function"); + } + if (typeof module !== "object") { + throw new ReferenceError("module is not an object"); + } + if (typeof exports !== "object") { + throw new ReferenceError("exports is not an object"); + } + if (module.exports !== exports) { + throw new Error("module.exports === exports returns false"); + } + if (typeof __dirname !== "string") { + throw new ReferenceError("__dirname is not a string"); + } + if (typeof __filename !== "string") { + throw new ReferenceError("__filename is not a string"); + } + if (typeof setTimeout !== typeOfGlobalFunc) { + throw new ReferenceError("setTimeout is not a function"); + } + if (typeof clearTimeout !== typeOfGlobalFunc) { + throw new ReferenceError("clearTimeout is not a function"); + } + if (typeof setInterval !== typeOfGlobalFunc) { + throw new ReferenceError("setInterval is not a function"); + } + if (typeof clearInterval !== typeOfGlobalFunc) { + throw new ReferenceError("clearInterval is not a function"); + } + if (typeof Error !== "function") { + throw new ReferenceError("Error is not a function"); + } + if (typeof parseFloat !== "function") { + throw new ReferenceError("parseFloat is not a function"); + } + if (typeof parseInt !== "function") { + throw new ReferenceError("parseInt is not a function"); + } + if (typeof window === "undefined") { + if (typeof process !== "object") { + throw new ReferenceError("process is not an object"); + } + if (typeof Buffer !== "function") { + throw new ReferenceError("Buffer is not a function"); + } + } else { + if (typeof encodeURIComponent !== "function") { + throw new ReferenceError("encodeURIComponent is not a function"); + } + if (typeof decodeURIComponent !== "function") { + throw new ReferenceError("decodeURIComponent is not a function"); + } + if (typeof document !== "object") { + throw new ReferenceError("document is not an object"); + } + } +} + +function getConsole() { + return console; +} + +function getFilename() { + return __filename; +} + +function getBuffer() { + return Buffer; +} + +function getDocument() { + return document; +} + +// different styles of exports in moduleA.js and moduleB.js +exports.setMyNumber = setMyNumber; +exports.getMyNumber = getMyNumber; +exports.setMyObj = setMyObj; +exports.getMyObj = getMyObj; +exports.readFileSync = readFileSync; +exports.checkSomeGlobals = checkSomeGlobals; +exports.getConsole = getConsole; +exports.getFilename = getFilename; +exports.getBuffer = getBuffer; +exports.getDocument = getDocument; +exports.someOtherModule = someOtherModule; \ No newline at end of file diff --git a/testLib/moduleB.js b/testLib/moduleB.js new file mode 100644 index 0000000..62b2d3f --- /dev/null +++ b/testLib/moduleB.js @@ -0,0 +1,137 @@ +"use strict"; // run code in ES5 strict mode + +var someOtherModule = require("./someOtherModule.js"), + myNumber = 0, // copy by value + myObj = {}, // copy by reference + env = "bla", + fs; + +// We need getters and setters for private vars to check if our injected setters and getters actual work +function setMyNumber(newNumber) { + myNumber = newNumber; +} + +function getMyNumber() { + return myNumber; +} + +function setMyObj(newObj) { + myObj = newObj; +} + +function getMyObj() { + return myObj; +} + +function readFileSync() { + fs.readFileSync("bla.txt", "utf8"); +} + +function checkSomeGlobals() { + var isLowerIE, + typeOfGlobalFunc; + + if (typeof navigator !== "undefined") { + isLowerIE = /MSIE [6-8]\.[0-9]/g.test(navigator.userAgent); + } + if (isLowerIE) { + typeOfGlobalFunc = "object"; + } else { + typeOfGlobalFunc = "function"; + } + + if (typeof global !== "object") { + throw new ReferenceError("global is not an object"); + } + if (typeof console !== "object") { + throw new ReferenceError("console is not an object"); + } + if (typeof require !== "function") { + throw new ReferenceError("require is not a function"); + } + if (typeof module !== "object") { + throw new ReferenceError("module is not an object"); + } + if (typeof exports !== "object") { + throw new ReferenceError("exports is not an object"); + } + if (module.exports === exports) { + throw new Error("module.exports === exports returns true"); + } + if (typeof __dirname !== "string") { + throw new ReferenceError("__dirname is not a string"); + } + if (typeof __filename !== "string") { + throw new ReferenceError("__filename is not a string"); + } + if (typeof setTimeout !== typeOfGlobalFunc) { + throw new ReferenceError("setTimeout is not a function"); + } + if (typeof clearTimeout !== typeOfGlobalFunc) { + throw new ReferenceError("clearTimeout is not a function"); + } + if (typeof setInterval !== typeOfGlobalFunc) { + throw new ReferenceError("setInterval is not a function"); + } + if (typeof clearInterval !== typeOfGlobalFunc) { + throw new ReferenceError("clearInterval is not a function"); + } + if (typeof Error !== "function") { + throw new ReferenceError("Error is not a function"); + } + if (typeof parseFloat !== "function") { + throw new ReferenceError("parseFloat is not a function"); + } + if (typeof parseInt !== "function") { + throw new ReferenceError("parseInt is not a function"); + } + if (typeof window === "undefined") { + if (typeof process !== "object") { + throw new ReferenceError("process is not an object"); + } + if (typeof Buffer !== "function") { + throw new ReferenceError("Buffer is not a function"); + } + } else { + if (typeof encodeURIComponent !== "function") { + throw new ReferenceError("encodeURIComponent is not a function"); + } + if (typeof decodeURIComponent !== "function") { + throw new ReferenceError("decodeURIComponent is not a function"); + } + if (typeof document !== "object") { + throw new ReferenceError("document is not an object"); + } + } +} + +function getConsole() { + return console; +} + +function getFilename() { + return __filename; +} + +function getBuffer() { + return Buffer; +} + +function getDocument() { + return document; +} + +// different styles of exports in moduleA.js and moduleB.js +module.exports = { + setMyNumber: setMyNumber, + getMyNumber: getMyNumber, + setMyObj: setMyObj, + getMyObj: getMyObj, + readFileSync: readFileSync, + checkSomeGlobals: checkSomeGlobals, + getConsole: getConsole, + getFilename: getFilename, + getBuffer: getBuffer, + getDocument: getDocument, + someOtherModule: someOtherModule +}; diff --git a/testLib/node_modules/rewire/package.json b/testLib/node_modules/rewire/package.json new file mode 100644 index 0000000..ea0fba5 --- /dev/null +++ b/testLib/node_modules/rewire/package.json @@ -0,0 +1,3 @@ +{ + "main": "../../../lib/index.js" +} diff --git a/testLib/sharedTestCases.js b/testLib/sharedTestCases.js new file mode 100644 index 0000000..9fb83ed --- /dev/null +++ b/testLib/sharedTestCases.js @@ -0,0 +1,357 @@ +// Don't run code in ES5 strict mode. +// In case this module was in strict mode, all other modules called by this would also be strict. +// But when testing if the strict mode is preserved, we must ensure that this module is NOT strict. + +// These shared test cases are used to check if the provided implementation of rewire is compatible +// with the original rewire. Since you can use rewire with client-side bundlers like webpack we need +// to test the implementation there again. +// @see https://github.com/jhnns/rewire-webpack + +var expect = require("expect.js"), + rewire = require("rewire"), + __set__Src = require("../lib/__set__.js").toString(), + __get__Src = require("../lib/__get__.js").toString(), + __with__Src = require("../lib/__with__.js").toString(); + +function checkForTypeError(err) { + expect(err.constructor).to.be(TypeError); +} + +describe("rewire " + (typeof testEnv === "undefined"? "(node)": "(" + testEnv + ")"), function () { + + it("should work like require()", function () { + rewire("./moduleA.js").getFilename(); + require("./moduleA.js").getFilename(); + expect(rewire("./moduleA.js").getFilename()).to.eql(require("./moduleA.js").getFilename()); + expect(rewire("../testLib/someOtherModule.js").filename).to.eql(require("../testLib/someOtherModule.js").filename); + }); + + it("should return a fresh instance of the module", function () { + var someOtherModule = require("./someOtherModule.js"), + rewiredSomeOtherModule; + + someOtherModule.fs = "This has been modified"; + rewiredSomeOtherModule = rewire("./someOtherModule.js"); + expect(rewiredSomeOtherModule.fs).not.to.be("This has been modified"); + }); + + it("should not cache the rewired module", function () { + var rewired, + someOtherModule = require("./someOtherModule.js"); + + someOtherModule.fs = "This has been changed"; + + rewired = rewire("./someOtherModule.js"); + expect(someOtherModule).not.to.be(rewired); + expect(require("./moduleA.js").someOtherModule).not.to.be(rewired); + expect(require("./moduleA.js").someOtherModule).to.be(someOtherModule); + expect(require("./moduleA.js").someOtherModule.fs).to.be("This has been changed"); + }); + + // By comparing the src we can ensure that the provided __set__ function is our tested implementation + it("should modify the module so it provides the __set__ - function", function () { + expect(rewire("./moduleA.js").__set__.toString()).to.be(__set__Src); + expect(rewire("./moduleB.js").__set__.toString()).to.be(__set__Src); + }); + + // By comparing the src we can ensure that the provided __set__ function is our tested implementation + it("should modify the module so it provides the __get__ - function", function () { + expect(rewire("./moduleA.js").__get__.toString()).to.be(__get__Src); + expect(rewire("./moduleB.js").__get__.toString()).to.be(__get__Src); + }); + + // By comparing the src we can ensure that the provided __set__ function is our tested implementation + it("should modify the module so it provides the __with__ - function", function () { + expect(rewire("./moduleA.js").__with__.toString()).to.be(__with__Src); + expect(rewire("./moduleB.js").__with__.toString()).to.be(__with__Src); + }); + + + ["__get__", "__set__", "__with__"].forEach(function(funcName) { + it("should provide " + funcName + " as a non-enumerable property", function () { + expect(Object.keys(rewire("./moduleA.js")).indexOf(funcName)).to.be(-1); + }); + + it("should provide " + funcName + " as a writable property", function () { + var obj = rewire("./moduleA.js"); + var desc = Object.getOwnPropertyDescriptor(obj, funcName); + expect(desc.writable).to.be(true); + }); + }); + + it("should not influence other modules", function () { + rewire("./moduleA.js"); + + expect(require("./someOtherModule.js").__set__).to.be(undefined); + expect(require("./someOtherModule.js").__get__).to.be(undefined); + expect(require("./someOtherModule.js").__with__).to.be(undefined); + }); + + it("should not override/influence global objects by default", function () { + // This should throw no exception + rewire("./moduleA.js").checkSomeGlobals(); + rewire("./moduleB.js").checkSomeGlobals(); + }); + + // This is just an integration test for the __set__ method + // You can find a full test for __set__ under /test/__set__.test.js + it("should provide a working __set__ method", function () { + var rewiredModuleA = rewire("./moduleA.js"), + newObj = {}; + + expect(rewiredModuleA.getMyNumber()).to.be(0); + rewiredModuleA.__set__("myNumber", 2); + expect(rewiredModuleA.getMyNumber()).to.be(2); + rewiredModuleA.__set__("myObj", newObj); + expect(rewiredModuleA.getMyObj()).to.be(newObj); + rewiredModuleA.__set__("env", "ENVENV"); + }); + + // This is just an integration test for the __get__ method + // You can find a full test for __get__ under /test/__get__.test.js + it("should provide a working __get__ method", function () { + var rewiredModuleA = rewire("./moduleA.js"); + + expect(rewiredModuleA.__get__("myNumber")).to.be(rewiredModuleA.getMyNumber()); + expect(rewiredModuleA.__get__("myObj")).to.be(rewiredModuleA.getMyObj()); + }); + + // This is just an integration test for the __with__ method + // You can find a full test for __with__ under /test/__with__.test.js + it("should provide a working __with__ method", function () { + var rewiredModuleA = rewire("./moduleA.js"), + newObj = {}; + + expect(rewiredModuleA.getMyNumber()).to.be(0); + expect(rewiredModuleA.getMyObj()).to.not.be(newObj); + + rewiredModuleA.__with__({ + myNumber: 2, + myObj: newObj + })(function () { + expect(rewiredModuleA.getMyNumber()).to.be(2); + expect(rewiredModuleA.getMyObj()).to.be(newObj); + }); + + expect(rewiredModuleA.getMyNumber()).to.be(0); + expect(rewiredModuleA.getMyObj()).to.not.be(newObj); + }); + + it("should provide the ability to inject mocks", function (done) { + var rewiredModuleA = rewire("./moduleA.js"), + mockedFs = { + readFileSync: function (file) { + expect(file).to.be("bla.txt"); + done(); + } + }; + + rewiredModuleA.__set__("fs", mockedFs); + rewiredModuleA.readFileSync(); + }); + + it("should not influence other modules when injecting mocks", function () { + var rewiredModuleA = rewire("./moduleA.js"), + someOtherModule, + mockedFs = {}; + + rewiredModuleA.__set__("fs", mockedFs); + someOtherModule = require("./someOtherModule.js"); + expect(someOtherModule.fs).not.to.be(mockedFs); + }); + + it("should provide the ability to mock global objects just within the module", function () { + var rewiredModuleA = rewire("./moduleA.js"), + rewiredModuleB = rewire("./moduleB.js"), + consoleMock = {}, + bufferMock = {}, + documentMock = {}, + newFilename = "myFile.js"; + + rewiredModuleA.__set__({ + console: consoleMock, + __filename: newFilename + }); + expect(rewiredModuleA.getConsole()).to.be(consoleMock); + expect(rewiredModuleB.getConsole()).not.to.be(consoleMock); + expect(console).not.to.be(consoleMock); + expect(rewiredModuleA.getFilename()).to.be(newFilename); + expect(rewiredModuleB.getFilename()).not.to.be(newFilename); + expect(console).not.to.be(newFilename); + if (typeof window === "undefined") { + rewiredModuleA.__set__("Buffer", bufferMock); + expect(rewiredModuleA.getBuffer()).to.be(bufferMock); + expect(rewiredModuleB.getBuffer()).not.to.be(bufferMock); + expect(Buffer).not.to.be(bufferMock); + } else { + rewiredModuleA.__set__("document", documentMock); + expect(rewiredModuleA.getDocument()).to.be(documentMock); + expect(rewiredModuleB.getDocument() === documentMock).to.be(false); + expect(document === documentMock).to.be(false); + } + }); + + it("should be possible to mock global objects that are added on runtime", function () { + var rewiredModule; + + if (typeof window === "undefined") { + global.someGlobalVar = "test"; + rewiredModule = rewire("./moduleA.js"); + 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.__set__("someGlobalVar", "other value"); + expect(window.someGlobalVar).to.be("test"); + expect(rewiredModule.__get__("someGlobalVar")).to.be("other value"); + if (typeof navigator !== "undefined" && /MSIE [6-8]\.[0-9]/g.test(navigator.userAgent) === false) { + delete window.someGlobalVar; + } + } + }); + + it("should not be a problem to have a comment on file end", function () { + var rewired = rewire("./emptyModule.js"); + + rewired.__set__("someVar", "hello"); + expect(rewired.__get__("someVar")).to.be("hello"); + }); + + it("should not influence the original require if nothing has been required within the rewired module", function () { + rewire("./emptyModule.js"); // nothing happens here because emptyModule doesn't require anything + expect(require("./moduleA.js").__set__).to.be(undefined); // if restoring the original node require didn't worked, the module would have a setter + }); + + it("subsequent calls of rewire should always return a new instance", function () { + expect(rewire("./moduleA.js")).not.to.be(rewire("./moduleA.js")); + }); + + it("should preserve the strict mode", function () { + var strictModule = rewire("./strictModule.js"); + + expect(function () { + strictModule.doSomethingUnstrict(); + }).to.throwException(checkForTypeError); + }); + + it("should not modify line numbers in stack traces", function () { + var throwError = rewire("./throwError.js"); + + if (process.env.running_under_istanbul === "1") { + return; + } + try { + throwError(); + } catch (err) { + if (err.stack) { + expect(err.stack.split("\n")[1]).to.match(/:2:11/); + } + } + }); + + it("should be possible to set implicit globals", function () { + var implicitGlobalModule, + err; + + try { + implicitGlobalModule = rewire("./implicitGlobal.js"); + + implicitGlobalModule.__set__("implicitGlobal", true); + expect(implicitGlobalModule.__get__("implicitGlobal")).to.be(true); + // setting implicit global vars will change them globally instead of locally. + // that's a shortcoming of the current implementation which can't be solved easily. + //expect(implicitGlobal).to.be.a("string"); + } catch (e) { + err = e; + } finally { + // Cleaning up... + delete global.implicitGlobal; + delete global.undefinedImplicitGlobal; + } + + if (err) { + throw err; + } + }); + + it("should throw a TypeError if the path is not a string", function () { + expect(function () { + rewire(null); + }).to.throwException(checkForTypeError); + }); + + it("should also revert nested changes (with dot notation)", function () { + var rewiredModuleA = rewire("./moduleA.js"), + revert; + + revert = rewiredModuleA.__set__("myObj.test", true); + expect(rewiredModuleA.getMyObj()).to.eql({ + test: true + }); + revert(); + // This test also demonstrates a known drawback of the current implementation + // If the value doesn't exist at the time it is about to be set, it will be + // reverted to undefined instead deleting it from the object + // However, this is probably not a real world use-case because why would you + // want to mock something when it is not set. + expect(rewiredModuleA.getMyObj()).to.eql({ + test: undefined + }); + + revert = rewiredModuleA.__set__({ + "myObj.test": true + }); + expect(rewiredModuleA.getMyObj()).to.eql({ + test: true + }); + revert(); + expect(rewiredModuleA.getMyObj()).to.eql({ + test: undefined + }); + + }); + + it("should be possible to mock undefined, implicit globals", function () { + var implicitGlobalModule, + err; + + try { + implicitGlobalModule = rewire("./implicitGlobal.js"); + implicitGlobalModule.__set__("undefinedImplicitGlobal", "yoo!"); + expect(implicitGlobalModule.__get__("undefinedImplicitGlobal")).to.equal("yoo!"); + + implicitGlobalModule = rewire("./implicitGlobal.js"); + implicitGlobalModule.__set__({ + undefinedImplicitGlobal: "bro!" + }); + expect(implicitGlobalModule.__get__("undefinedImplicitGlobal")).to.equal("bro!"); + } catch (e) { + err = e; + } finally { + // Cleaning up... + delete global.implicitGlobal; + delete global.undefinedImplicitGlobal; + } + + if (err) { + throw err; + } + }); + + it("should be possible to mock and revert JSON.parse (see #40)", function () { + var moduleA = rewire("./moduleA.js"), + revert; + + revert = moduleA.__set__({ + JSON: { + parse: function () { return true; } + } + }); + + revert(); + }); + +}); diff --git a/testLib/someOtherModule.js b/testLib/someOtherModule.js new file mode 100644 index 0000000..da38f4c --- /dev/null +++ b/testLib/someOtherModule.js @@ -0,0 +1,6 @@ +"use strict"; // run code in ES5 strict mode + +__filename = "/test/testModules/someOtherModule.js"; + +exports.fs = {}; +exports.filename = __filename; \ No newline at end of file diff --git a/testLib/strictModule.js b/testLib/strictModule.js new file mode 100644 index 0000000..5d6e387 --- /dev/null +++ b/testLib/strictModule.js @@ -0,0 +1,7 @@ +"use strict"; // run code in ES5 strict mode + +function doSomethingUnstrict() { + var caller = arguments.callee.caller; // this should throw an error as a proof that strict mode is on +} + +exports.doSomethingUnstrict = doSomethingUnstrict; \ No newline at end of file diff --git a/testLib/throwError.js b/testLib/throwError.js new file mode 100644 index 0000000..9bdf68b --- /dev/null +++ b/testLib/throwError.js @@ -0,0 +1,3 @@ +module.exports = function () { + throw new Error(); +}; -- cgit v1.2.3