How to mock a global commonjs method with jest? - javascript

I try to test some basic redux reducers with jest and stumbled about a problem I can't solve on my own:
In my reducer I'm referencing a global method "__" (some i18n stuff), which implementation basically looks like the following:
window.__ = function(foo) { return window.i18n[foo]; }
The mentioned method is referenced in another module I include in my reducer via import definitions from 'definitions';
The definitions file looks like the following:
/* global __ */
// some basic stuff
export default () {
return __('foobar');
}
How is it possible to mock the __ method within the test code? Given that __ is not a module it can't be automocked. :/

You can use the following syntax to mock the global __ method:
global.__ = function(foo) { return foo; };

Related

Automatic mock of an ES6 class fails (Jest, Vanilla JavaScript)

I have the class FooStorage that has an Array of Foo objects as a member variable. When testing FooStorage I want to mock the class Foo.
As described at ES6 Class Mocks, an automatic mock is all I need in my case. But when I try to mock the class, it doesn't seem to succeed. Instead when I try to reset the mock with mockClear(), I recieve an error message.
Below is the code and the output from jest:
foo.js
class Foo {};
export default Foo;
foostorage.js
import Foo from "./foo.js";
class FooStorage {
constructor() {
this.storage = []; // Array of Foo objects
}
}
export default FooStorage;
foostorage.test.js
import Foo from "../src/foo.js";
import FooStorage from "../src/foostorage.js";
import { jest } from "#jest/globals";
jest.mock("../src/foo.js");
beforeEach(() => {
Foo.mockClear();
});
test("if the Foo constructor hasn`t been called", () => {
const storage = new FooStorage();
expect(Foo).not.toHaveBeenCalled();
});
output
if the Foo constructor hasn`t been called
TypeError: Foo.mockClear is not a function
7 |
8 | beforeEach(() => {
> 9 | Foo.mockClear();
| ^
10 | });
11 |
12 | test("if the Foo constructor hasn`t been called", () => {
at Object.<anonymous> (test/foostorage.test.js:9:6)
I have already tried to put jest.mock("../src/foo.js"); before import Foo from "../src/foo.js"; but the problem wasn't solved.
Edit:
I am using Jest v. 27.0.6 with jest-environment-node v. 27.0.6 and #types/jest v. 27.0.1.
I also use the nodejs arguments --experimental-modules and --experimental-vm-modules so that I can use ES6 imports. I don`t use Babel or anything else. Just plain JavaScript.
Automatic mocking is supposed to work like the documentation explains. If it doesn't and results in said error, this means that the module wasn't mocked, so it would fail with manual mock as well. This can happen because a mock wasn't performed correctly (different case or extension in module path), or jest.mock hoisting didn't work (most commonly because of misconfigured Babel transform).
The way this code differs from what's shown in the documentation is that jest global is commonly used, while here it is imported value, so it's not the same as using jest global. This is obvious and known reason for hoisting to work differently.
It can possibly be fixed by ordering dependencies in a way that doesn't interfere with expected execution order:
import { jest } from "#jest/globals";
jest.mock("../src/foo.js");
import Foo from "../src/foo.js";
...
The hoisting of jest.mock relies on undocumented, spec-incompliant hack that also depends on Jest version and a specific setup. It's not guaranteed to work because ES imports are expected to hoisted above other statements by specs, and there are no exclusions.
A dated but stable workaround is to switch to require that are regular JavaScript function calls and are evaluated as is:
let { jest } = require ("#jest/globals");
jest.mock("../src/foo.js");
let { default: Foo } = require("../src/foo.js");
...
And an efficient solution is to get rid of jest import and set up a generic Jest environment with respective globals, so jest.mock could be hoisted as expected regardless of Jest version and other circumstances.

Importing all exports in a module NodeJS

I want to be able to access all exports of a module without having to say module. before the export.
Let's say that I have a module:
// mymod.js
module.exports.foo = function() {
console.log("foo!");
}
module.exports.bar = "bar!";
And a main file:
// main.js
var mymod = require("./mymod.js");
mymod.foo();
Is there a way to call foo() without needing to say mymod. before? This can be achieved in python by saying import module as *.
What is the NodeJS equivalent to this?
In ES6 you can import modules in the following ways
import moduleName from "path/to/module"; // import default export from the file as moduleName object, moduleName can be anything
import { exportMemberName1, exportMemberName2, ... } from "path/to/module"; // destructured import, it will destructure import and can access the export module without prefixing anything
import * as moduleName from "path/to/module"; // import everything exported from the file as moduleName object, you can access every export members from that object, moduleName can be anything
These are the only methods provided by ES6 to import module (you can also use require).
If you have to import 100s of modules best ways is first method, import everything as an object and destructure on the go, I meant if you have lots of functions or methods, destructure what you want in that function in side that function, eg.
import * as moduleName from "path/to/file";
function function1(){
const { exportMember1, exportMember2 } = module;
}
function function2(){
const { exportMember1, exportMember5, exportMember7 } = module;
}
I want to be able to access all exports of a module without having to
say module. before the export.
Use the shorthand:
exports.myVar = myVar
exports.foo = () => {}
Or use an Object:
module.exports = {
foo,
myVar
}
// main.js
var mymod = require("./mymod.js");
mymod.foo();
Is there a way to call foo() without needing to say mymod. before?
This can be achieved in python by saying import module as *. What is
the NodeJS equivalent to this?
Use destructuring:
const { foo } = require("./mymod.js")
lets say that I have 100 exports in a file. Do I need to put commas
after every import inside the { }? There must be a better way to do
this
If you have 100 exports why would you want to import them all globally as their own functions? myMod.func is better for clarity.
A hacky workaround might be to do const myMod = require('myMod') then map it putting the functions on the global object. Or put them on the global from the start instead of exporting it.
You can use ES6 destructuring:
var { foo } = require("./mymod.js");
foo();
I have a situation where a I have a tiny-but-not-that-tiny generic utilities that is used along a couple of modules (all it's functions are used), in which there is a decent amount of modules already loaded. This functions are obviously named in a way you know there are a part of a generic utilities modules, so the "module.function" it's redundant, does not improve the readeability of the code. So, I prefered to mimick the "import * from module" of Python. Note that this is the first time I come across this situation, therefore, IMO, this mechanism, in almost every case, is not a good practice at all. The only way to do that, is iterating over the exports of the module, and adding the functions to the global object. I made a function to make the intention clear.
const importAll = () => {
return {
mod: null,
from(modName) {
this.mod = require(modName);
Object.keys(this.mod)
.forEach(exportedElementId => global[exportedElementId] = this.mod[exportedElementId]);
}
}
}
And it is used like this:
importAll().from('module-name');
Note that this only works if the module exports an object. Wont work if the module exports, for example, an array.
Here is another way, which may be a bit cleaner and more convenient in some cases: method importAll() is implemented inside export-heavy module, so it might be called immediately after require(), making this call very brief.
This works really well for large modules stuffed with simple standard functions and constants that are used across multiple projects.
Example:
// module.js
'use strict';
function func1() { return '4'; };
function func2() { return 2; };
function importAll() { delete this.importAll; Object.assign(global, this); };
module.exports = { func1, func2, importAll };
Then, in the main app, module can be unwrapped as follows:
// app.js
'use strict';
require('./module').importAll();
console.log("result: '%d'", func1() + func2());
There are few caveats though:
since properties/methods are added to global object, those might override some existing properties/methods, so be careful with naming.
those properties/methods will become available EVERYWHERE: in all modules, sub-modules, etc, so no need to call require() more than once.

ES6 module import a function value [duplicate]

Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters

What are extra advantage of using ES6 module instead of revealing module pattern?

I am exploring ES6 module and trying to figure out what extra advantage we get using ES6 module instead of closure along with module pattern(MP).
For example
util.js in ES6.
var util ={
abc:function(){
//function body
},
def:function(){
// function body
}
export default utils; // here export is exposing the entire object
}
util.js using closure & module pattern
var util = (function(){
function _abc(){
console.log("abc")
// function body
};
function _def(){
// function body
}
return{ // each of the function will be exposed
abc:_abc,
def:_def
}
}(util ||{}))
someFile.js in ES6
import {utils} from "path/to/file"
In someFile.js with closure & module pattern
util.abc() // Will log "abc"
Also I know es6 module allow us to rename imports & exports
Like export { a as abc} .
With closure & module pattern we can give a name whatever we like inside return statement like return { a:_abc}
My question:What extra benefit we can get by using es6 module instead of closure& MP.One i guess is reduction in lines of code.
Please excuse me if I have missed any basic difference
With var util = (function(){ bla bla bla }(util || {})); the global namespace is polluted, so that once you have used import {utils} from "path/to/file", it will remain in global namespace, i.e. you'll be have window.util everywhere, even after the module has finished it's work and replace by some other module. Now consider you have 100s of modules and you do it in the same way, then imagine how dirty would the poor window become!
However if ES6 Module or CommonJS or even AMD is used, then
The global namespace is not polluted.
[ES6] You can use export default something to export a default value to use import from "path/to/file"
[ES6] You can export multiple things from ES6 Module, using export["anotherthing"]
Furthermore I would recommend you to read this blog post.

is there any way to obtain a reference to (and use) an es6/2015 import in the same expression? [duplicate]

Is it possible to pass options to ES6 imports?
How do you translate this:
var x = require('module')(someoptions);
to ES6?
There is no way to do this with a single import statement, it does not allow for invocations.
So you wouldn't call it directly, but you can basically do just the same what commonjs does with default exports:
// module.js
export default function(options) {
return {
// actual module
}
}
// main.js
import m from 'module';
var x = m(someoptions);
Alternatively, if you use a module loader that supports monadic promises, you might be able to do something like
System.import('module').ap(someoptions).then(function(x) {
…
});
With the new import operator it might become
const promise = import('module').then(m => m(someoptions));
or
const x = (await import('module'))(someoptions)
however you probably don't want a dynamic import but a static one.
Concept
Here's my solution using ES6
Very much inline with #Bergi's response, this is the "template" I use when creating imports that need parameters passed for class declarations. This is used on an isomorphic framework I'm writing, so will work with a transpiler in the browser and in node.js (I use Babel with Webpack):
./MyClass.js
export default (Param1, Param2) => class MyClass {
constructor(){
console.log( Param1 );
}
}
./main.js
import MyClassFactory from './MyClass.js';
let MyClass = MyClassFactory('foo', 'bar');
let myInstance = new MyClass();
The above will output foo in a console
EDIT
Real World Example
For a real world example, I'm using this to pass in a namespace for accessing other classes and instances within a framework. Because we're simply creating a function and passing the object in as an argument, we can use it with our class declaration likeso:
export default (UIFramework) => class MyView extends UIFramework.Type.View {
getModels() {
// ...
UIFramework.Models.getModelsForView( this._models );
// ...
}
}
The importation is a bit more complicated and automagical in my case given that it's an entire framework, but essentially this is what is happening:
// ...
getView( viewName ){
//...
const ViewFactory = require(viewFileLoc);
const View = ViewFactory(this);
return new View();
}
// ...
I hope this helps!
Building on #Bergi's answer to use the debug module using es6 would be the following
// original
var debug = require('debug')('http');
// ES6
import * as Debug from 'debug';
const debug = Debug('http');
// Use in your code as normal
debug('Hello World!');
I've landed on this thread looking up for somewhat similar and would like to propose a sort of solution, at least for some cases (but see Remark below).
Use case
I have a module, that is running some instantiation logic immediately upon loading. I do not like to call this init logic outside the module (which is the same as call new SomeClass(p1, p2) or new ((p1, p2) => class SomeClass { ... p1 ... p2 ... }) and alike).
I do like that this init logic will run once, kind of a singular instantiation flow, but once per some specific parametrized context.
Example
service.js has at its very basic scope:
let context = null; // meanwhile i'm just leaving this as is
console.log('initialized in context ' + (context ? context : 'root'));
Module A does:
import * as S from 'service.js'; // console has now "initialized in context root"
Module B does:
import * as S from 'service.js'; // console stays unchanged! module's script runs only once
So far so good: service is available for both modules but was initialized only once.
Problem
How to make it run as another instance and init itself once again in another context, say in Module C?
Solution?
This is what I'm thinking about: use query parameters. In the service we'd add the following:
let context = new URL(import.meta.url).searchParams.get('context');
Module C would do:
import * as S from 'service.js?context=special';
the module will be re-imported, it's basic init logic will run and we'll see in the console:
initialized in context special
Remark: I'd myself advise to NOT practice this approach much, but leave it as the last resort. Why? Module imported more than once is more of an exception than a rule, so it is somewhat unexpected behavior and as such may confuse a consumers or even break it's own 'singleton' paradigms, if any.
I believe you can use es6 module loaders.
http://babeljs.io/docs/learn-es6/
System.import("lib/math").then(function(m) {
m(youroptionshere);
});
You just need to add these 2 lines.
import xModule from 'module';
const x = xModule('someOptions');
Here's my take on this question using the debug module as an example;
On this module's npm page, you have this:
var debug = require('debug')('http')
In the line above, a string is passed to the module that is imported, to construct. Here's how you would do same in ES6
import { debug as Debug } from 'debug'
const debug = Debug('http');
Hope this helps someone out there.
I ran into an analogous syntax issue when trying to convert some CJS (require()) code to ESM (import) - here's what worked when I needed to import Redis:
CJS
const RedisStore = require('connect-redis')(session);
ESM Equivalent
import connectRedis from 'connect-redis';
const RedisStore = connectRedis(session);
You can pass parameters in the module specifier directly:
import * as Lib from "./lib?foo=bar";
cf: https://flaming.codes/en/posts/es6-import-with-parameters

Categories

Resources