UNPKG

15.9 kBJavaScriptView Raw
1// Don't run code in ES5 strict mode.
2// In case this module was in strict mode, all other modules called by this would also be strict.
3// But when testing if the strict mode is preserved, we must ensure that this module is NOT strict.
4
5// These shared test cases are used to check if the provided implementation of rewire is compatible
6// with the original rewire. Since you can use rewire with client-side bundlers like webpack we need
7// to test the implementation there again.
8// @see https://github.com/jhnns/rewire-webpack
9
10var expect = require("expect.js"),
11 rewire = require("rewire"),
12 __set__Src = require("../lib/__set__.js").toString(),
13 __get__Src = require("../lib/__get__.js").toString(),
14 __with__Src = require("../lib/__with__.js").toString();
15
16var supportsObjectSpread = (function () {
17 try {
18 eval("({...{}})");
19 return true;
20 } catch (err) {
21 return false;
22 }
23})();
24var supportsObjectRest = (function () {
25 try {
26 eval("const {...a} = {}");
27 return true;
28 } catch (err) {
29 return false;
30 }
31})();
32
33function checkForTypeError(err) {
34 expect(err.constructor).to.be(TypeError);
35}
36
37module.exports = function () {
38
39 it("should work like require()", function () {
40 rewire("./moduleA.js").getFilename();
41 require("./moduleA.js").getFilename();
42 expect(rewire("./moduleA.js").getFilename()).to.eql(require("./moduleA.js").getFilename());
43 expect(rewire("../testLib/someOtherModule.js").filename).to.eql(require("../testLib/someOtherModule.js").filename);
44 });
45
46 it("should return a fresh instance of the module", function () {
47 var someOtherModule = require("./someOtherModule.js"),
48 rewiredSomeOtherModule;
49
50 someOtherModule.fs = "This has been modified";
51 rewiredSomeOtherModule = rewire("./someOtherModule.js");
52 expect(rewiredSomeOtherModule.fs).not.to.be("This has been modified");
53 });
54
55 it("should not cache the rewired module", function () {
56 var rewired,
57 someOtherModule = require("./someOtherModule.js");
58
59 someOtherModule.fs = "This has been changed";
60
61 rewired = rewire("./someOtherModule.js");
62 expect(someOtherModule).not.to.be(rewired);
63 expect(require("./moduleA.js").someOtherModule).not.to.be(rewired);
64 expect(require("./moduleA.js").someOtherModule).to.be(someOtherModule);
65 expect(require("./moduleA.js").someOtherModule.fs).to.be("This has been changed");
66 });
67
68 // By comparing the src we can ensure that the provided __set__ function is our tested implementation
69 it("should modify the module so it provides the __set__ - function", function () {
70 expect(rewire("./moduleA.js").__set__.toString()).to.be(__set__Src);
71 expect(rewire("./moduleB.js").__set__.toString()).to.be(__set__Src);
72 });
73
74 // By comparing the src we can ensure that the provided __set__ function is our tested implementation
75 it("should modify the module so it provides the __get__ - function", function () {
76 expect(rewire("./moduleA.js").__get__.toString()).to.be(__get__Src);
77 expect(rewire("./moduleB.js").__get__.toString()).to.be(__get__Src);
78 });
79
80 // By comparing the src we can ensure that the provided __set__ function is our tested implementation
81 it("should modify the module so it provides the __with__ - function", function () {
82 expect(rewire("./moduleA.js").__with__.toString()).to.be(__with__Src);
83 expect(rewire("./moduleB.js").__with__.toString()).to.be(__with__Src);
84 });
85
86
87 ["__get__", "__set__", "__with__"].forEach(function(funcName) {
88 it("should provide " + funcName + " as a non-enumerable property", function () {
89 expect(Object.keys(rewire("./moduleA.js")).indexOf(funcName)).to.be(-1);
90 });
91
92 it("should provide " + funcName + " as a writable property", function () {
93 var obj = rewire("./moduleA.js");
94 var desc = Object.getOwnPropertyDescriptor(obj, funcName);
95 expect(desc.writable).to.be(true);
96 });
97 });
98
99 it("should not influence other modules", function () {
100 rewire("./moduleA.js");
101
102 expect(require("./someOtherModule.js").__set__).to.be(undefined);
103 expect(require("./someOtherModule.js").__get__).to.be(undefined);
104 expect(require("./someOtherModule.js").__with__).to.be(undefined);
105 });
106
107 it("should not override/influence global objects by default", function () {
108 // This should throw no exception
109 rewire("./moduleA.js").checkSomeGlobals();
110 rewire("./moduleB.js").checkSomeGlobals();
111 });
112
113 // This is just an integration test for the __set__ method
114 // You can find a full test for __set__ under /test/__set__.test.js
115 it("should provide a working __set__ method", function () {
116 var rewiredModuleA = rewire("./moduleA.js"),
117 newObj = {};
118
119 expect(rewiredModuleA.getMyNumber()).to.be(0);
120 rewiredModuleA.__set__("myNumber", 2);
121 expect(rewiredModuleA.getMyNumber()).to.be(2);
122 rewiredModuleA.__set__("myObj", newObj);
123 expect(rewiredModuleA.getMyObj()).to.be(newObj);
124 rewiredModuleA.__set__("env", "ENVENV");
125 });
126
127 // This is just an integration test for the __get__ method
128 // You can find a full test for __get__ under /test/__get__.test.js
129 it("should provide a working __get__ method", function () {
130 var rewiredModuleA = rewire("./moduleA.js");
131
132 expect(rewiredModuleA.__get__("myNumber")).to.be(rewiredModuleA.getMyNumber());
133 expect(rewiredModuleA.__get__("myObj")).to.be(rewiredModuleA.getMyObj());
134 });
135
136 // This is just an integration test for the __with__ method
137 // You can find a full test for __with__ under /test/__with__.test.js
138 it("should provide a working __with__ method", function () {
139 var rewiredModuleA = rewire("./moduleA.js"),
140 newObj = {};
141
142 expect(rewiredModuleA.getMyNumber()).to.be(0);
143 expect(rewiredModuleA.getMyObj()).to.not.be(newObj);
144
145 rewiredModuleA.__with__({
146 myNumber: 2,
147 myObj: newObj
148 })(function () {
149 expect(rewiredModuleA.getMyNumber()).to.be(2);
150 expect(rewiredModuleA.getMyObj()).to.be(newObj);
151 });
152
153 expect(rewiredModuleA.getMyNumber()).to.be(0);
154 expect(rewiredModuleA.getMyObj()).to.not.be(newObj);
155 });
156
157 it("should provide the ability to inject mocks", function (done) {
158 var rewiredModuleA = rewire("./moduleA.js"),
159 mockedFs = {
160 readFileSync: function (file) {
161 expect(file).to.be("bla.txt");
162 done();
163 }
164 };
165
166 rewiredModuleA.__set__("fs", mockedFs);
167 rewiredModuleA.readFileSync();
168 });
169
170 it("should not influence other modules when injecting mocks", function () {
171 var rewiredModuleA = rewire("./moduleA.js"),
172 someOtherModule,
173 mockedFs = {};
174
175 rewiredModuleA.__set__("fs", mockedFs);
176 someOtherModule = require("./someOtherModule.js");
177 expect(someOtherModule.fs).not.to.be(mockedFs);
178 });
179
180 it("should provide the ability to mock global objects just within the module", function () {
181 var rewiredModuleA = rewire("./moduleA.js"),
182 rewiredModuleB = rewire("./moduleB.js"),
183 consoleMock = {},
184 bufferMock = {},
185 documentMock = {},
186 newFilename = "myFile.js";
187
188 rewiredModuleA.__set__({
189 console: consoleMock,
190 __filename: newFilename
191 });
192 expect(rewiredModuleA.getConsole()).to.be(consoleMock);
193 expect(rewiredModuleB.getConsole()).not.to.be(consoleMock);
194 expect(console).not.to.be(consoleMock);
195 expect(rewiredModuleA.getFilename()).to.be(newFilename);
196 expect(rewiredModuleB.getFilename()).not.to.be(newFilename);
197 expect(console).not.to.be(newFilename);
198 if (typeof window === "undefined") {
199 rewiredModuleA.__set__("Buffer", bufferMock);
200 expect(rewiredModuleA.getBuffer()).to.be(bufferMock);
201 expect(rewiredModuleB.getBuffer()).not.to.be(bufferMock);
202 expect(Buffer).not.to.be(bufferMock);
203 } else {
204 rewiredModuleA.__set__("document", documentMock);
205 expect(rewiredModuleA.getDocument()).to.be(documentMock);
206 expect(rewiredModuleB.getDocument() === documentMock).to.be(false);
207 expect(document === documentMock).to.be(false);
208 }
209 });
210
211 it("should be possible to mock global objects that are added on runtime", function () {
212 var rewiredModule;
213
214 if (typeof window === "undefined") {
215 global.someGlobalVar = "test";
216 rewiredModule = rewire("./moduleA.js");
217 rewiredModule.__set__("someGlobalVar", "other value");
218 expect(global.someGlobalVar).to.be("test");
219 expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
220 delete global.someGlobalVar;
221 } else {
222 window.someGlobalVar = "test";
223 rewiredModule = rewire("./moduleA.js");
224 rewiredModule.__set__("someGlobalVar", "other value");
225 expect(window.someGlobalVar).to.be("test");
226 expect(rewiredModule.__get__("someGlobalVar")).to.be("other value");
227 if (typeof navigator !== "undefined" && /MSIE [6-8]\.[0-9]/g.test(navigator.userAgent) === false) {
228 delete window.someGlobalVar;
229 }
230 }
231 });
232
233 it("should not be a problem to have a comment on file end", function () {
234 var rewired = rewire("./emptyModule.js");
235
236 rewired.__set__("someVar", "hello");
237 expect(rewired.__get__("someVar")).to.be("hello");
238 });
239
240 it("should not be a problem to have a module that exports a boolean", function( ) {
241 rewire("./boolean.js"); // should not throw
242 });
243
244 it("should not be a problem to have a module that exports null", function () {
245 rewire("./null.js"); // should not throw
246 });
247
248 it("should not be a problem to have a module that exports a sealed object", function () {
249 rewire("./sealedObject.js"); // should not throw
250 });
251
252 (supportsObjectSpread ? it : it.skip)("should not be a problem to have a module that uses object spread operator", function () {
253 rewire("./objectSpreadOperator.js"); // should not throw
254 });
255
256 (supportsObjectRest ? it : it.skip)("should not be a problem to have a module that uses object rest operator", function () {
257 rewire("./objectRestOperator.js"); // should not throw
258 });
259
260 it("should not influence the original require if nothing has been required within the rewired module", function () {
261 rewire("./emptyModule.js"); // nothing happens here because emptyModule doesn't require anything
262 expect(require("./moduleA.js").__set__).to.be(undefined); // if restoring the original node require didn't worked, the module would have a setter
263 });
264
265 it("subsequent calls of rewire should always return a new instance", function () {
266 expect(rewire("./moduleA.js")).not.to.be(rewire("./moduleA.js"));
267 });
268
269 it("should preserve the strict mode", function () {
270 var strictModule = rewire("./strictModule.js");
271
272 expect(function () {
273 strictModule.doSomethingUnstrict();
274 }).to.throwException(checkForTypeError);
275 });
276
277 it("should not modify line numbers in stack traces", function () {
278 var throwError = rewire("./throwError.js");
279
280 try {
281 throwError();
282 } catch (err) {
283 expect(err.stack.split("\n")[1]).to.match(/:6:26/);
284 }
285 });
286
287 it("should be possible to set implicit globals", function () {
288 var implicitGlobalModule,
289 err;
290
291 try {
292 implicitGlobalModule = rewire("./implicitGlobal.js");
293
294 implicitGlobalModule.__set__("implicitGlobal", true);
295 expect(implicitGlobalModule.__get__("implicitGlobal")).to.be(true);
296 // setting implicit global vars will change them globally instead of locally.
297 // that's a shortcoming of the current implementation which can't be solved easily.
298 //expect(implicitGlobal).to.be.a("string");
299 } catch (e) {
300 err = e;
301 } finally {
302 // Cleaning up...
303 delete global.implicitGlobal;
304 delete global.undefinedImplicitGlobal;
305 }
306
307 if (err) {
308 throw err;
309 }
310 });
311
312 it("should throw a TypeError if the path is not a string", function () {
313 expect(function () {
314 rewire(null);
315 }).to.throwException(checkForTypeError);
316 });
317
318 it("should also revert nested changes (with dot notation)", function () {
319 var rewiredModuleA = rewire("./moduleA.js"),
320 revert;
321
322 revert = rewiredModuleA.__set__("myObj.test", true);
323 expect(rewiredModuleA.getMyObj()).to.eql({
324 test: true
325 });
326 revert();
327 // This test also demonstrates a known drawback of the current implementation
328 // If the value doesn't exist at the time it is about to be set, it will be
329 // reverted to undefined instead deleting it from the object
330 // However, this is probably not a real world use-case because why would you
331 // want to mock something when it is not set.
332 expect(rewiredModuleA.getMyObj()).to.eql({
333 test: undefined
334 });
335
336 revert = rewiredModuleA.__set__({
337 "myObj.test": true
338 });
339 expect(rewiredModuleA.getMyObj()).to.eql({
340 test: true
341 });
342 revert();
343 expect(rewiredModuleA.getMyObj()).to.eql({
344 test: undefined
345 });
346
347 });
348
349 it("should be possible to mock undefined, implicit globals", function () {
350 var implicitGlobalModule,
351 err;
352
353 try {
354 implicitGlobalModule = rewire("./implicitGlobal.js");
355 implicitGlobalModule.__set__("undefinedImplicitGlobal", "yoo!");
356 expect(implicitGlobalModule.__get__("undefinedImplicitGlobal")).to.equal("yoo!");
357
358 implicitGlobalModule = rewire("./implicitGlobal.js");
359 implicitGlobalModule.__set__({
360 undefinedImplicitGlobal: "bro!"
361 });
362 expect(implicitGlobalModule.__get__("undefinedImplicitGlobal")).to.equal("bro!");
363 } catch (e) {
364 err = e;
365 } finally {
366 // Cleaning up...
367 delete global.implicitGlobal;
368 delete global.undefinedImplicitGlobal;
369 }
370
371 if (err) {
372 throw err;
373 }
374 });
375
376 it("should be possible to mock and revert JSON.parse (see #40)", function () {
377 var moduleA = rewire("./moduleA.js"),
378 revert;
379
380 revert = moduleA.__set__({
381 JSON: {
382 parse: function () { return true; }
383 }
384 });
385
386 revert();
387 });
388
389 it("should be possible to set a const variable", function () {
390 var constModule = rewire("./constModule");
391 var varNames = Object.keys(constModule);
392
393 expect(varNames.length).to.be.greaterThan(0);
394
395 varNames.forEach(varName => {
396 constModule.__set__(varName, "this has been changed"); // should not throw
397 expect(constModule[varName]()).to.equal("this has been changed");
398 });
399 });
400
401 it("should fail with a helpful TypeError when const is re-assigned", function () {
402 expect(function () {
403 rewire("./wrongConstModule");
404 }).to.throwException(/^Assignment to constant variable at .+?wrongConstModule\.js:4:1$/);
405 });
406
407};