Not able to load AMD modules through Jest - requirejs

I'm trying to use Jest for unit testing my React code but I'm also using requirejs and so all my React code is in AMD modules. It obviously works well in browser but when I run Jest tests, it can't load my AMD modules.
Initially it was giving me error for define saying define is not defined, so I used amdefine by Requirejs to fix it. Now it can understand define but still can't load my module.

I had the same problem today and here is what I've done :
Module.js
(()=> {
const dependencies = ['./dep.js'];
const libEnv = function (dep) {
// lib content
function theAnswer (){
return dep.answer;
}
return {theAnswer}; // exports
};
//AMD & CommonJS compatibility stuff
// CommonJS
if (typeof module !== 'undefined' && typeof require !== 'undefined'){
module.exports = libEnv.apply(this, dependencies.map(require));
module.exports.mockable = libEnv; // module loader with mockable dependencies
}
// AMD
if (typeof define !== 'undefined') define(dependencies, libEnv);
})();
You will found all needed files on my github repository to test :
in browser for requirejs
with node for jest
https://github.com/1twitif/testRequireJSAmdModulesWithJest

I ran into same problem, so I went ahead and mocked my dependency at the top of test file:
jest.mock('../../components/my-button', () => {
// mock implementation
})
import MyButton from '../../components/my-button';
This way, when MyButton is loaded, its dependency is already mocked, so it won't try to load the RequireJS module.

There's no official Facebook support for requirejs in Jest yet. But's planned. Look this thread:
https://github.com/facebook/jest/issues/17
Also in this thread Sterpe posted a plugin he wrote to do it (but I didn't try it):
https://github.com/sterpe/jest-requirejs

Related

Code coverage for UMD block in nodejs module

I've written a node module with UMD (Universal Module Definition) Pattern to make it compatible with amd and plain javascript as well.
The definition is as below
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.myModule = factory();
}
}(this, function (global) {
...
return {};
}));
With mocha & chai the tests are running fine, the only issue is that since the tests are executed from within node the code coverage for define(factory); and root.myModule = factory(); lines is showing red.
Just wanted to know if there is any way(maybe a hack) to get 100% coverage for this code with mocha chai.
Sure!
How coverage tools for JS basically work, is that their injecting little bit of tracker code for each line. So what you need is that the execution flow wonders there.
Since this is an if-else logic, to get 100% you will be needing multiple test scenarios to cover this.
Note: it's also important for actually achieving this effect, that loading that file (factory) should happen after every scenario setup, because that is when those module-loading-if-else lines are executed. Now this depends on what is your file/module loading in your test execution environment.
Now to enter into that branch of the if-else create a test scenario where you can just enforce the existence of that define function with a few lines like (e.g. in a beforeEach section):
define = function () {
console.log('hello I am a fake define, but I will be defined ...');
console.log(' ... so that if else branch will be executed.');
}
// and of course the needed .amd property.
define.amd = true;
I think the same weird stuff can be done w/ the exports variable, but it might be hard to achieve if you are just pure CommonJS require-ing that file, since require-ing defines the exports variable for that file context, so maybe you could modify your production code for this weird case. I wouldn't do that, but you said any way:
...
} else if (typeof exports === 'object' && testExportsEnabled) {
...
Now using this you can just have one scenario where in beforeEach you do: testExportsEnabled = true; and w/ this being false, the last branch should be executed.
Of course you can execute all branches with such a simple trick! Now to have no code modification AND trigger the third branch (basically the case for simple script-tag-loading in browser), you will need to dig in into your module loading code and create some hacks there, but that is a bit too much for me in an SO question! :]
Take care!

How does jest allow mutation of modules?

In this question that I asked here:
Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?
I'm asking about the nature of module mutation.
However as it it turns out, ES6 modules can't actually be mutated - all of their properties are treated as constants. (See this answer)
But somehow - when Jest tests modules - they can be mutated, and that's how Jest allows for mocking.
How is this happening?
I imagine that it's a babel plugin that that's running - transpiling the module to CommonJS modules? Is there any documentation about this?
Is there a way to view the transpiled code?
ES6 modules can't actually be mutated - all of their properties are treated as constants.
Interesting. You're right, even something as simple as this:
import * as lib from "./lib"; // import an ES6 module
const spy = jest.spyOn(lib, 'someFunc'); // spy on someFunc
...technically shouldn't be allowed since jest.spyOn replaces the method on the object with a spy and lib.someFunc should be a binding to someFunc in the ES6 module.
But somehow - when Jest tests modules - they can be mutated, and that's how Jest allows for mocking.
How is this happening?
They can only be mutated because Jest isn't actually using ES6 modules.
(I guess for completeness it might be possible to run Jest using actual ES6 modules by using Node's experimental support for ES6 Modules but I haven't tried).
I imagine that it's a babel plugin that that's running - transpiling the module...Is there any documentation about this?
"babel-jest is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the transform configuration option".
So by default Jest will use babel-jest which transpiles the source code using babel (and does a few other things like hoisting calls to jest.mock).
Note that Jest can be also be configured using transform which maps "regular expressions to paths to transformers".
Is there a way to view the transpiled code?
Yes. Transformations are done in jest-runtime here and the output is saved to a cache here.
The easiest way to look at the transpiled code is to view the cache.
You can do that by running Jest with the --showConfig option which will output the config used when running Jest. The cache location can be found by looking at the value of "cacheDirectory".
Then run Jest with the --clearCache option to clear out the cache.
Finally, run Jest normally and the cache directory will contain the transpiled code for your project.
Example
The latest Jest (v24) will transpile this code:
// lib.js
export const someFunc = () => 1;
// code.js
import { someFunc } from './lib';
export const func = () => someFunc() + 1;
// code.test.js
import { func } from './code';
import * as lib from './lib';
test('func', () => {
const spy = jest.spyOn(lib, 'someFunc');
func();
expect(spy).toHaveBeenCalled(); // SUCCESS
});
...to this:
// lib.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.someFunc = void 0;
const someFunc = () => 1;
exports.someFunc = someFunc;
// code.js
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.func = void 0;
var _lib = require("./lib");
const func = () => (0, _lib.someFunc)() + 1;
exports.func = func;
// code.test.js
"use strict";
var _code = require("./code");
var lib = _interopRequireWildcard(require("./lib"));
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
test('func', () => {
const spy = jest.spyOn(lib, 'someFunc');
(0, _code.func)();
expect(spy).toHaveBeenCalled(); // SUCCESS
});
The import * as lib from 'lib'; line gets handled by _interopRequireWildcard which uses require under the hood.
Every call to require "will get exactly the same object returned, if it would resolve to the same file" so code.js and code.test.js are getting the same object from require('./lib').
someFunc is exported as exports.someFunc which allows it to be reassigned.
So yes, you're exactly right. Spying (or mocking) like this only works because the ES6 modules are getting transpiled by babel into Node modules in a way that allows them to be mutated.

Why is `window` undefined when running my Webpack plug-in after upgrading to Webpack 4?

I've written a plug-in for Webpack that takes my generated React component, renders it in Node, and then inserts it into the generated HTML document.
This used to work fine in Webpack 3. To upgrade it to Webpack 4, I replaced
compiler.plugin("after-emit", compilation => {
with
compiler.hooks.afterEmit.tap('prerender-plugin', (compilation) => {
which, as far as I can see, should be sufficient.
In the plug-in, I ask Webpack for the path to my generated bundle and then require that, roughly as follows:
const assetHash = Object.keys(compilation.assets).filter(asset => /app(.*).js/.test(asset))[0];
const appFilePath = compilation.assets[assetHash].existsAt;
// The `.default` is needed because it's an ES2015 module
const App = require(appFilePath).default;
However, I then all of a sudden hit errors like the following as a result of the require:
ReferenceError: window is not defined
at Object.<anonymous> (/home/vincent/Workspace/Flockademic/stacks/frontend/dist/app.da5512cf55a3c4446086.js:1:286)
...
The offending code seems to be some standard Webpack top matter:
!function(e,i){if("object"==typeof exports&&"object"==typeof module)module.exports=i();else if("function"==typeof define&&define.amd)define([],i);else{var a=i();for(var o in a)("object"==typeof exports?exports:e)[o]=a[o]}}(window,function(){return function(e){var i={};function a(o){if
(Note that this sample is up to the supposedly-offending column. As you can see, window is not actually referred to on column 286.)
I'm not that familiar with Webpack, and documentation on plug-ins relatively scarce, so any pointers on how to get closer to solving this are very much welcome/
Add this at your root .js file will probably work.
if (typeof window === 'undefined') {
global.window = {};
}

In Electron, using "typeof" causes TypeError: Cannot assign to read only property

I got a doozie here. In Electron's main process, I require in the function below to setup event handlers with ipcMain. This keeps the main.js file a little more streamlined. All went swimmingly until I wrote some validation code to ensure that the user passes in an object. I use typeof all the time for this purpose, and never have I had an issue. But in Electron I am getting:
A JavaScript error occurred in the main process - TypeError: Cannot
assign to read only property 'exports' of object '# '
The code:
const {ipcMain} = require('electron');
function ipcSetup() {
ipcMain.on('123', function(event, arg) {
// this blows chunks...
if(arg && typeof arg === 'object') {
console.log(`All good....`);
}
// and if you comment that out, this use of "typeof" does the same thing
console.log(typeof arg);
// and to eliminate 'arg' as the issue...
let a = 1;
console.log(typeof a); // expect 'number', get Exception
});
}
module.exports = ipcSetup;
I didn't know if Electron is using Object.defineProperty to make arg read only, but typeof is not making an assignment here anyway, and I eliminated arg so this error makes no sense. Environment is Electron 1.8.4 on Node 8.2.1 using electron-vue
If you're importing that module in another module that uses ES6 import/export syntax, using typeof apparently causes this. I think this is because webpack doesn't support mixing the two syntaxes. But it is quite funny that it would work fine if you didn't use the typeof operator...

Detect if called through require or directly by command line

How can I detect whether my Node.JS file was called using SH:node path-to-file or JS:require('path-to-file')?
This is the Node.JS equivalent to my previous question in Perl: How can I run my Perl script only if it wasn't loaded with require?
if (require.main === module) {
console.log('called directly');
} else {
console.log('required as a module');
}
See documentation for this here: https://nodejs.org/docs/latest/api/modules.html#modules_accessing_the_main_module
There is another, slightly shorter way (not outlined in the mentioned docs).
var runningAsScript = !module.parent;
I outlined more details about how this all works under the hood in this blog post.
For those using ES Modules (and Node 10.12+), you can use import.meta.url:
import path from 'path';
import { fileURLToPath } from 'url'
const nodePath = path.resolve(process.argv[1]);
const modulePath = path.resolve(fileURLToPath(import.meta.url))
const isRunningDirectlyViaCLI = nodePath === modulePath
Things like require.main, module.parent and __dirname/__filename aren’t available in ESM.
Note: If using ESLint it may choke on this syntax, in which case you’ll need to update to ESLint ^7.2.0 and turn your ecmaVersion up to 11 (2020).
More info: process.argv, import.meta.url
I was a little confused by the terminology used in the explanation(s). So I had to do a couple quick tests.
I found that these produce the same results:
var isCLI = !module.parent;
var isCLI = require.main === module;
And for the other confused people (and to answer the question directly):
var isCLI = require.main === module;
var wasRequired = !isCLI;
Try this if you are using ES6 modules:
if (process.mainModule.filename === __filename) {
console.log('running as main module')
}
I always find myself trying to recall how to write this goddamn code snippet, so I decided to create a simple module for it. It took me a bit to make it work since accessing caller's module info is not straightforward, but it was fun to see how it could be done.
So the idea is to call a module and ask it if the caller module is the main one. We have to figure out the module of the caller function. My first approach was a variation of the accepted answer:
module.exports = function () {
return require.main === module.parent;
};
But that is not guaranteed to work. module.parent points to the module which loaded us into memory, not the one calling us. If it is the caller module that loaded this helper module into memory, we're good. But if it isn't, it won't work. So we need to try something else. My solution was to generate a stack trace and get the caller's module name from there:
module.exports = function () {
// generate a stack trace
const stack = (new Error()).stack;
// the third line refers to our caller
const stackLine = stack.split("\n")[2];
// extract the module name from that line
const callerModuleName = /\((.*):\d+:\d+\)$/.exec(stackLine)[1];
return require.main.filename === callerModuleName;
};
Save this as is-main-module.js and now you can do:
const isMainModule = require("./is-main-module");
if (isMainModule()) {
console.info("called directly");
} else {
console.info("required as a module");
}
Which is easier to remember.
First, let's define the problem better. My assumption is that what you are really looking for is whether your script owns process.argv (i.e. whether your script is responsible for processing process.argv). With this assumption in mind, the code and tests below are accurate.
module.parent works excellently, but it is deprecated for good reasons (a module might have multiple parents, in which case module.parent only represents the first parent), so use the following future-proof condition to cover all cases:
if (
typeof process === 'object' && process && process.argv
&& (
(
typeof module === 'object' && module
&& (
!module.parent
|| require.main === module
|| (process.mainModule && process.mainModule.filename === __filename)
|| (__filename === "[stdin]" && __dirname === ".")
)
)
|| (
typeof document === "object"
&& (function() {
var scripts = document.getElementsByTagName("script");
try { // in case we are in a special environment without path
var normalize = require("path").normalize;
for (var i=0,len=scripts.length|0; i < len; i=i+1|0)
if (normalize(scripts[i].src.replace(/^file:/i,"")) === __filename)
return true;
} catch(e) {}
})()
)
)
) {
// this module is top-level and invoked directly by the CLI
console.log("Invoked from CLI");
} else {
console.log("Not invoked from CLI");
}
It works correctly in all of the scripts in all of the following cases and never throws any errors†:
Requiring the script (e.x. require('./main.js'))
Directly invoking the script (e.x. nodejs cli.js)
Preloading another script (e.x. nodejs -r main.js cli.js)
Piping into node CLI (e.x. cat cli.js | nodejs)
Piping with preloading (e.x. cat cli.js | nodejs -r main.js)
In workers (e.x. new Worker('./worker.js'))
In evaled workers (e.x. new Worker('if (<test for CLI>) ...', {eval: true}))
Inside ES6 modules (e.x. nodejs --experimental-modules cli-es6.js)
Modules with preload (e.x. nodejs --experimental-modules -r main-es6.js cli-es6.js)
Piped ES6 modules (e.x. cat cli-es6.js | nodejs --experimental-modules)
Pipe+preload module (e.x. cat cli-es6.js | nodejs --experimental-modules -r main-es6.js)
In the browser (in which case, CLI is false because there is no process.argv)
In mixed browser+server environments (e.x. ElectronJS, in which case both inline scripts and all modules loaded via <script> tags are considered CLI)
The only case where is does not work is when you preload the top-level script (e.x. nodejs -r cli.js cli.js). This problem cannot be solved by piping (e.x. cat cli.js | nodejs -r cli.js) because that executes the script twice (once as a required module and once as top-level). I do not believe there is any possible fix for this because there is no way to know what the main script will be from inside a preloaded script.
† Theoretically, errors might be thrown from inside of a getter for an object (e.x. if someone were crazy enough to do Object.defineProperty(globalThis, "process", { get(){throw 0} });), however this will never happen under default circumstances for the properties used in the code snippet in any environment.
How can I detect whether my node.js file was called directly from console (windows and unix systems) or loaded using the ESM module import ( import {foo} from 'bar.js')
Such functionality is not exposed. For the moment you should separate your cli and library logic into separate files.
Answer from node.js core contributor devsnek replying to nodejs/help/issues/2420
It's the right answer in my point of view

Resources