Create manual mock for #material-ui withStyles in React with Jest - javascript

I am trying to create manual mocks for the #material-ui/core/styles module, which exports the HOC called withStyles.
Using the inline mock with jest.mock works perfectly, but when I try to move this logic into sharable __mocks__ folder, it does not work anymore.
I removed from the file being tested all the unecessary code, just keeping the two lines creating the issues:
import { withStyles } from '#material-ui/core/styles';
console.log('loaded styles:', withStyles);
export default 'hello';
And the test simplified as well, just to see if the mock is working, is the following:
import test from '../test';
console.log(test);
What I have tried is to create a __mocks__ folder in the root of the project, where I have the node_modules folder.
There I created a first folder called #material-ui and inside it another folder called core.
Inside this folder I have a file called styles.js with the following code:
export const withStyles = 'hello world';
So the structure looks like:
- __mocks__
- #material-ui
- core
- styles.js
- node_modules
This is the code which works, with the mock defined inside the same testing file:
jest.mock('#material-ui/core/styles', () => ({
withStyles: () => Component => props => (
<Component
classes=""
{...props}
/>
),
}));
What is happening with the __mocks__ folder is that, if I do not call any
jest.mock('#material-ui/core/styles');
inside my test file, it uses the real withStyles, so the real module.
No mock at all is being used.
If I use the:
jest.mock('#material-ui/core/styles');
It uses an automatic mock autogenerated by jest (or it looks so), skipping the one I define above.
Just a note about packages, I used CRA for bootstrapping the app and the jest version I currently have is the 20, with the react-scripts version 1.0.17
Thanks to you all for you the help!

Here is how you should use manual mocking in __mocks__/#material-ui/core/styles.js:
// Grab the original exports
import * as Styles from '#material-ui/core/styles';
const mockWithStyles = () => {
console.log('withStyles being mocked'); // this shows that it works
/**
* Note: if you want to mock this return value to be
* different within a test suite then use
* the pattern defined here:
* https://jestjs.io/docs/en/manual-mocks
*/
return () => {}; // your mock function (adjust as needed)
// you can also return the same implementation on certain conditions
// return Styles.makeStyles;
};
module.exports = { ...Styles, withStyles: mockWithStyles };
Here is how I manual mock makeStyles:
// Grab the original exports
// eslint-disable-next-line import/no-extraneous-dependencies
import * as Styles from '#material-ui/core/styles';
import createMuiTheme from '#material-ui/core/styles/createMuiTheme';
import options from '../../../src/themes/options';
const mockMakeStyles = func => {
/**
* Note: if you want to mock this return value to be
* different within a test suite then use
* the pattern defined here:
* https://jestjs.io/docs/en/manual-mocks
*/
/**
* Work around because Shallow rendering does not
* Hook context and some other hook features.
* `makeStyles` accept a function as argument (func)
* and that function accept a theme as argument
* so we can take that same function, passing it as
* parameter to the original makeStyles and
* binding it with our custom theme
*/
const theme = createMuiTheme(options);
return Styles.makeStyles(func.bind(null, theme));
};
module.exports = { ...Styles, makeStyles: mockMakeStyles };
And makeStyles result uses React.useContext, so we have to avoid mocking useContext for makeStyles. Either use mockImplementationOnce if you use React.useContext(...) first in you component, or just filter it out in your test code as:
jest.spyOn(React, 'useContext').mockImplementation(context => {
console.log(context);
// only stub the response if
if (context.displayName === 'MyAppContext') {
return {
auth: {},
lang: 'en',
snackbar: () => {},
};
}
const ActualReact = jest.requireActual('react');
return ActualReact.useContext(context);
});
And on your createContext() call, probably in a store.js, add a displayName (standard), or any other custom property to Identify your context:
const store = React.createContext(initialState);
store.displayName = 'MyAppContext';
The makeStyles context displayName will appear as StylesContext and ThemeContext and their implementation will remain untouched to avoid error.
This fixed all kind of mocking problem with makeStyles. But I haven't use withStyles to know its structure deeply, but similar logic should be applicable.

I created the file src/__mocks__/#material-ui/core/styles.jsx in my project and inside I put:
import * as Styles from '#material-ui/core/styles';
import React from 'react';
const withStyles = () => Component => props => <Component classes={{}} {...props} />;
module.exports = { ...Styles, withStyles };

Related

Using vue.use in a library own

I'm using the vue-sfc-rollup lib to create a lib for Vue, so far it's ok, my lib is working, and I can use it in other projects, however, a doubt has arisen.
In which file could I add in my lib the use of bootstrap? to understand better I'm putting below the structure that the SFC generated for me..
The file entry.js
// iife/cjs usage extends esm default export - so import it all
import plugin, * as components from '#/entry.esm';
// Attach named exports directly to plugin. IIFE/CJS will
// only expose one global var, with component exports exposed as properties of
// that global var (eg. plugin.component)
Object.entries(components).forEach(([componentName, component]) => {
if (componentName !== 'default') {
plugin[componentName] = component;
}
});
export default plugin;
the file entry.esm.js
// Import vue components
import * as components from '#/lib-components/index';
// install function executed by Vue.use()
const install = function installZicketVueCheckout(Vue) {
Object.entries(components).forEach(([componentName, component]) => {
Vue.component(componentName, component);
});
};
// Create module definition for Vue.use()
export default install;
// To allow individual component use, export components
// each can be registered via Vue.component()
export * from '#/lib-components/index';

What does `import type {Node} from 'react';` and usage in App: () => Node

Running this command:
npx react-native init AwesomeProject
In the App.js file I don't understand 2 lines:
import React from 'react';
import type {Node} from 'react'; // 1
import {
SafeAreaView,
ScrollView,
// ..... Code ....
const App: () => Node = () => { // 2
// ..... Code ....
export default App;
Importing type Node
Following Is there a point to doing 'import type' rather than 'import' with Flow?, I understand that such import is used to import a type of object, for instance:
import type { Array, Object, ... } from 'wherever';
To be honest I am more concerned on the next point (probably If I understand that I would automatically get the this as well).
const App: () => Node = () =>
All that I see is that App is a variable which references a function that returns an Object of type Node which this Object s also a function. Does it wrap the App into a 'React' instance or something?
What does const App: () => Node = () => do and why you would want to use it?
If we remove the types, the code is:
const App = () => {
// ... code
}
This is a react functional component.
Then on top of that is added a type: () => Node. This means it's a function that takes no parameters and returns a react Node. Types help with development by letting the computer analyze the code better, and point out bugs sooner (before even running the code)

Jest mocked implementation not being called outside of test function

I have a 3rd party module I need to mock (moment.js). I want to set the implementation to the default implementation before requiring the file I'm testing. The only function I'm mocking is the default export of the module, so I assign the prototype and static members to those of the actual implementation, as well.
season.js
import moment from 'moment';
export var currentSeason = getCurrentSeason();
export function currentSeasion() {
const diff = moment().diff(/* ... */);
// ...
return /* a number */;
}
__tests__/season.js
import moment from 'moment';
jest.mock('moment');
const actualMoment = jest.requireActual('moment');
moment.mockImplementation((...args) => actualMoment(...args));
Object.assign(moment, actualMoment);
const { getCurrentSeason } = require('../season');
test('getCurrentSeason()', () => {
moment.mockReturnValue(actualMoment(/* ... */));
expect(getCurrentSeason()).toBe(1);
});
I confirmed via debugging that mockImpelementation() is being called correctly, and within the test, it's being called correctly too. However, in the initialization of currentSeason, moment() is returning undefined. When I stepped into the moment() mocked function, mockConfig.mockImpl is undefined.
Running expect(moment()).toBeUndefined() in the test file but outside of any test before the import of season.js runs the mock implementation, as well.
I can't for the life of me figure out why it's only not working in the initialization of currentSeason.
I don't know how useful this will be for anyone else, but my solution turned out to be to pull my mock code into it's own /__mocks__/moment.js file.
const actual = jest.requireActual('moment'),
moment = jest.fn((...args) => actual(...args));
export default Object.assign(moment, actual);

Importing reusable actions in Vuex

I'm working on a NUXT project and I find myself copying the same actions into multiple store/modules. So I have extracted the actions to a separate file and I now import it into the module. That works great but I occasionally need an additional action that is not boilerplate. How do I import the boilerplate actions and also have a custom action in the module?
Tags module:
import Vue from "vue";
import globalActions from "../utils/Actions.js";
export const state = () => ({ /* removed */ })
export const actions = globalActions;
//Need actions to be a bit more flexible to include non-boilerplate actions
I'm not sure it matters but here is utils/Actions.js It is just the standard "export default {}" that would typically be in the module.
export default {
all({ commit }, all) {
all.data.forEach(item => {
commit("add", item);
});
},
async list({ commit, state, getters, dispatch }) {
/* flush resets the state */
commit("flush");
/*Makes the api call using the repository setup */
let params = getters.params;
const results = await this.$repositories[state.type].index(params);
const ids = results.data.map(item => item.id);
let page = state.page;
dispatch("all", results);
/*Adds a encyclopedia that maps pages to index */
commit("SET_REFERENCE", { page, ids });
commit("totalItems", results.meta.total);
},
}
Ideally, I think the module actions would look something like this:
export const actions = {
list(){ return globalActions.list }
nonBoilerPlateAction({commit})
}
I am pretty sure I will need to change how I import the globalActions and that my "ideal" actions syntax is wrong but I not sure what I need to adjust.
To merge the imported actions and the custom actions, you can merge the two actions in this manner:
export const actions = {
...globalActions,
nonBoilerPlateAction({commit}) {
commit('something')
}
}
Although the way you are using to re-use your module works fine, I'll recommend using namespacing instead. This way all your created module can be easily re-used without having to import them to any other file. You can have access to other modules from another module easily.

How to Mock non-default import using Jest

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
});

Categories