I have a module that imports using ES6 object destructuring.
foo.js:
import { render } from 'react-dom';
I want to mock render in a unit test. I've tried two things, but neither of them seem to work.
Using a simple stub:
import * as reactDom from 'react-dom';
let renderStub = sinon.stub(reactDom, 'render');
Someone suggested proxyquire, but I'm not sure of the correct way to stub a method from a node module. This is what I attempted:
import proxyquire from 'proxyquire';
proxyquire.noCallThru();
let foo = proxyquire('./foo', {
'react-dom': {
render: () => {}
}
});
How do I mock this method correctly?
Related
TL;DR How can I use esbuild with ReactJS.NET?
Long version
ReactJS.NET expects the following from a "bundle":
React.Exceptions.ReactNotInitialisedException: 'React has not been loaded correctly: missing (React, ReactDOM, ReactDOMServer). Please expose your version of React as global variables named 'React', 'ReactDOM', and 'ReactDOMServer'
What Webpack actually does is always quite unclear to me, but looking at the ReactJS.NET Webpack tutorial here, this is the setup:
import React from 'react';
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
import RootComponent from './home.jsx';
global.React = React;
global.ReactDOM = ReactDOM;
global.ReactDOMServer = ReactDOMServer;
global.Components = { RootComponent };
Further at the sample webpack-config here, there is this output-setting:
{
[...]
globalObject: 'this',
}
Mirroring this in esbuild would be using iife and esbuild.globalName = 'this', but it throws an error:
> (global name):1:0: error: Expected identifier but found "this"
1 │ this
Esbuild is quite stern on that it is a bundler (not a transpile-and-concatenator), so how would I tweak Esbuild to have the bundle mutate the global object to what React.NET expects? - And is it even possible?
Thanks,
Flemming
Found a solution.
import { build } from 'esbuild'
const globalName = 'whatever'
build({
[...]
format: 'iife',
globalName,
footer: {
// Important! Assigns the raw export of the bundle to whatever `this` is in the given context
js: `Object.assign(this, ${globalName})`,
},
})
How can I mock a function that is being imported inside the file that contains the function I am testing?
without putting it in the mocks folder.
// FileIWantToTest.js
import { externalFunction } from '../../differentFolder';
export const methodIwantToTest = (x) => { externalFunction(x + 1) }
I need to make sure that externalFunction is called and its called with the correct arguments.
Seems super simple but the documentation doesn't cover how to do this without mocking the module for all the files in the test by putting the mocks in the mocks folder.
The solution: I need to take my jest.mock call outside of any test or any other function because jest needs to hoist it. Also for named exports like in my case I also have to use the following syntax:
jest.mock('../../differentFolder', () => ({
__esModule: true,
externalFunction: jest.fn(),
}));
One of the easiest approaches is to import the library and use jest.spyOn to spy on the method:
import { methodIwantToTest } from './FileIWantToTest';
import * as lib from '../../differentFolder'; // import the library containing externalFunction
test('methodIwantToTest', () => {
const spy = jest.spyOn(lib, 'externalFunction'); // spy on externalFunction
methodIwantToTest(1);
expect(spy).toHaveBeenCalledWith(2); // SUCCESS
});
I want to do something like this, but using import rather than require:
const MySubmodule = require('react-native').MyModule.MySubmodule;
I tried:
import { MySubmodule } from 'react-native/MyModule';
import { MySubmodule } from ('react-native').MyModule;
import { MySubmodule } from 'react-native'.MyModule;
None of these works.
So any idea how to import a module contained in another using import?
You will have to import MyModule completely, and can then separately destructure it to get the parts you are interested in:
import {MyModule} from 'react-native';
const {MySubmodule} = MyModule;
The import statement does not support directly destructuring exports. See also this Babel issue for some more info.
Is there any way to import and export multiple files using for-of-loop (or another loop) in ES6?
const moduleNames = ['NumberUtils', 'StringUtils', 'ArrayUtils', 'MyModule', 'AnotherModule', 'BaseModule']
let modules = {}
for (const moduleName of moduleNames) {
import module from './' + moduleName
modules.moduleName = module
}
export modules
Without loop I have to write:
import NumberUtils from './NumberUtils'
import StringUtils from './StringUtils'
import ArrayUtils from './ArrayUtils'
import MyModule from './MyModule'
import AnotherModule from './AnotherModule'
import BaseModule from './BaseModule'
export {
NumberUtils,
StringUtils
ArrayUtils
MyModule
AnotherModule
BaseModule
}
One of main features of ES modules is they can be statically analyzed. For this reason import statement follows strict syntax - so does export. A snippet 'without loop' is the way it has to be done.
This allows to figure out module imports and exports exactly in IDEs and tools. This is useful for tree-shaking, for instance.
I think that better and more clear way to do it is to create an index file and then import multiple components in one import.
//index.js
import PopUp from './PopUp';
import ToggleSwitch from './ToggleSwitch';
export {
PopUp,
ToggleSwitch
};
//app.js
import { PopUp, ToggleSwitch } from './components';
For multiple import files I found this solution:
const files = require.context('../myFolder', true, /(Module|Utils)\.js$/)
I am getting a ReferenceError: NC_SETTINGS is not defined.
"use strict";
import forOwn from 'lodash/object/forOwn';
import { assert, expect, should } from 'chai';
import { spy } from 'sinon';
import { applyMiddleware } from 'redux';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import {
REQUEST_ADD_PASSWORD_RESET_REQUEST,
REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS,
REQUEST_ADD_PASSWORD_RESET_REQUEST_FAILURE
} from 'actions/types';
import nock from 'nock';
import Actions from 'actions';
import fetch from 'isomorphic-fetch';
const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);
var NC_SETTINGS = require('/home/tai/Gravitate/nc-app/taimoor/settings').NC_SETTINGS;
describe('forgot password async actions', () => {
afterEach(()=> {
nock.cleanAll();
});
//mockActions();
//console.log(Actions.addPasswordResetRequest().addPasswordResetRequestAPI());
it('should show a request for a password reset and that it succeeded ', (done) => {
console.log(NC_SETTINGS);
nock('http://localhost:8080/')
.post('/password-reset-requests')
.reply(200);
var email = "test#email.com";
const expectedActions= [
{type: REQUEST_ADD_PASSWORD_RESET_REQUEST},
{type: REQUEST_ADD_PASSWORD_RESET_REQUEST_SUCCESS}
];
const store = mockStore({}, expectedActions, done);
store.dispatch(Actions.addPasswordResetRequest());
//unMockActions();
//
//console.log(actions);
//expect(actions.addPasswordResetRequest("email")).to.equal(expectedAction);
});
});
So when I console.log NC_SETTINGS, it does show. However, when I run store.dispatch(Actions.addPasswordResetRequest), I get the NC_SETTINGS is not defined. The reason why I thought importing NC_SETTINGS could potentially work is because it worked for importing isomorphic-fetch, I had a similar issue.
Is there a way to import global variables into MochaJs such that my action can access NC_SETTINGS?
I should mention that store.dispatch(Actions.addPasswordResetRequest()) links to an actions file, which dispatches to an api call in a js file. That js file at the end is where NC_SETTINGS resides, and where the error is being called. In the browser, this works fine.
Is there a way to have a list of global variables and add them while testing in Mocha?
Don't do this, but you can use:
global.NC_SETTINGS = ...
to set it as a nodejs global variable. And this should be picked up in Actions.
However global variables are something you want to avoid for many, many reasons. Instead you probably want to do some kind of dependency injection. Two possibilities are:
Implement the Actions as a class, and instantiate it where it's used passing NC_SETTINGS as a construction parameter.
Use the rewire module. In that case NC_SETTINGS could be a top level variable in the Actions module, but not a global variable.