mikeborozdin / jest-react-hooks-shallow Goto Github PK
View Code? Open in Web Editor NEWBringing React Hooks to shallow rendering
License: ISC License
Bringing React Hooks to shallow rendering
License: ISC License
How can I add this option via package.json file jest config?
I recently came across your package in hopes that it would solve my shallow + hooks testing hole I'm currently in. However I seem to be running into issues getting the mock hooks to actually stay in place.
I've created a fork and I'll make a PR to this repository adding a example into the samples-and-e2e-tests folder in this project.
PR: #33
I am using useEffect
in my test component.
const Cmp = () => {
React.useEffect(() => {
console.log(1);
return () => console.log(2);
}, []);
return <span>Test</span>;
};
And in test: shallow(<Cmp />).unmount()
.
And there is no log emitted in the cleanup function
Looks good. I am struggling to make the jest import with Typescript though.
If a component has more than one useEffect hook, the callback will be invoked only for the very first test in a describe
block.
A very simple reproduction:
function Component() {
useEffect(() => {
console.log('test 1');
}, []);
useEffect(() => {
console.log('test 2');
}, []);
return null;
}
describe('test', () => {
it('useEffect works', () => {
shallow(<Component />);
});
it('useEffect does not work', () => {
shallow(<Component />);
});
})
Note that if I remove t he deps array everything will be back to normal.
Hello!
This is a really useful library. Thanks for making it. While trying to integrate this, I realized the steps suggested in the docs don't work as expected if the jest config as resetMocks
set to true
: https://jestjs.io/docs/en/configuration#resetmocks-boolean
My setupHooks.js
file:
import enableHooks from 'jest-react-hooks-shallow';
enableHooks(jest);
It seems that the resetMocks
option clears the mocks setup by this library before every test. I fixed it by using the now deprecated reenableHooks
function in the following way:
import enableHooks, { reenableHooks } from 'jest-react-hooks-shallow';
enableHooks(jest);
beforeEach(() => {
reenableHooks();
});
I also tried putting just the enableHooks
in a beforeEach
block, but didn't seem to work. The above combination is the only thing that worked for me.
I'm wondering if you would consider "re-introducing" the deprecated function since it seems to solve for a particular case? Also, do you mind sharing why it was deprecated?
i'm guessing you don't want to tie this library to enzyme, but if you did, you could spy on mount and basically change it to
withoutHooks(() => {
originalMount(...)
});
Hello.
When I use this library to do the unit testing with the shallow object, I found that the cleanup function of the first effect will be triggered by the second effect. Is this under our expectations?
Experiment code below:
import React from 'react';
import { shallow } from 'enzyme';
const Cmp = () => {
React.useEffect(() => {
console.log(1);
return () => console.log(2);
}, []);
React.useEffect(() => {
console.log(3);
return () => console.log(4);
}, []);
return <span>Test</span>;
};
it('test it', () => {
shallow(<Cmp/>);
});
and the console will output "1", "2", and "3". But our expectation is only "1" and "3".
Could you help me check this situation? Thanks a lot.
After including this library and adding the setup, able to pass tests fromuseEffect
actions seamlessly which I couldn't do with other solutions.
But soon, all the other test components (where mount
wrapper of enzyme is used and the components are usingmaterial-ui
components) started throwing throwing the error Cannot read property 'refs' of undefined
error.
Sample Test Component:
//TestComponent.js
import React, { useState } from 'react';
import TextField from '@material-ui/core/TextField';
const TestComponent = () => {
return (
<TextField value="somedata" />
}
}
export default TestComponent;
//TestComponent.test.js
import React from 'react';
import { mount } from 'enzyme';
import TestComponent from '../TestComponent';
describe('TestComponent', () => {
Date.now = jest.fn(() => 123);
it('matches snapshot', () => {
const componentWrapper = mount(<TestComponent />);
expect(componentWrapper).toMatchSnapshot();
});
});
But, Not getting error when i try using useRef and ForwardRef's inside the component
Not getting error with material-ui components too when I exclude this package from setup..
Any work around for this would be welcomed?
It doesn't seem to work with jest 27, has anyone succeeded?
Just giving credit for the idea/hack of using the function body as the key to figure out which effect call is which here:
I borrowed that strategy for this adapter that enables shallow testing of effects at the adapter level: https://github.com/davidmfoley/enzyme-adapter-react-16-with-shallow-effects/blob/master/src/enableEffects.js#L2
Thanks!
From what I gather (still processing, apologizes if my understanding is incorrect) this seems to be a stop-gap measure until React supports Enzyme with some sort of event regarding effect callbacks which is effectively tracked via enzymejs/enzyme#2086.
However the documentation for this package doesn't reference this issue which I think would be useful for additional context if people are interested... or if the issue does ever actually get resolved, as a back reference to what the official solution ends up being.
Just something to consider, thanks!
Thank you for this wonderfull piece of code, it's going to change my life !
I used the code to test it in a jasmine integration. For now I managed to make it work in a test like this.
it('should enter use effect', () => {
type FunctionBody = string;
type CleanupFunction = () => void;
const noDepsOrDifferent = (previousDependencies: unknown[], currentDependencies: unknown[]): boolean => {
return (
previousDependencies === undefined ||
previousDependencies.some((prevDep, index) => prevDep !== currentDependencies[index])
);
};
spyOn(React, 'useEffect').and.callFake(
(effect: () => CleanupFunction | void, dependencies?: unknown[]): void => {
const previousCalls = new Map<FunctionBody, unknown[]>();
const cleanupFunctions = new Map<string, CleanupFunction>();
const effectBody = effect.toString();
const shouldCall = previousCalls.has(effectBody)
? noDepsOrDifferent(previousCalls.get(effectBody), dependencies)
: true;
if (shouldCall) {
previousCalls.set(effectBody, dependencies);
if (cleanupFunctions.has(effectBody)) {
cleanupFunctions.get(effectBody)();
}
const cleanupFunction = effect();
if (typeof cleanupFunction === 'function') {
cleanupFunctions.set(effectBody, cleanupFunction);
}
}
}
);
shallowMountTheComponent();
expect(SpyingAFunctionInComponentUseEffectHook).toHaveBeenCalledTimes(1);
});
But I cannot seem able to trigger the cleanup function. Since useEffect
is not triggered when the shallow component unmounts, How would you trigger the cleanupFunction ?
tried to use this on a react-native project, does this support react-native atm?
Hi,
We've recently installed this package and it's brilliant! However, we noticed that when running the package with storybook and creating storyshots there's an error: TypeError: Cannot read property '0' of undefined.
The origin of this error is this function, namely the third line:
const noDepsOrDifferent = (previousDependencies, currentDependencies) => { return previousDependencies === undefined || previousDependencies.some((prevDep, index) => prevDep !== currentDependencies[index]); };
Although currentDepdencies can be undefined, there is no check for it - therefore the index line throws an error. The storyshots passed with this check:
previousDependencies.some((prevDep, index) => prevDep !== (currentDependencies && currentDependencies[index]));
Is that an appropriate solution? I'm not entirely sure what dependencies are in this instance (with the exception of it being dependencies from useEffect?) or why it would be undefined here.
Thank you!
I've just installed jest-react-hooks-shallow and added enableHooks(jest, { dontMockByDefault: true })
in my test setup script, and I'm seeing countless test failures in tests that use mount()
, multiple errors several similar to this:
Warning: React has detected a change in the order of Hooks called by Formik. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks
Previous render Next render
------------------------------------------------------
1. useRef useRef
2. useRef useRef
3. useRef useRef
4. useRef useRef
5. useRef useRef
6. useRef useRef
7. useReducer useEffect
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
I'm using react-scripts (create-react-app), so I had to add enableHooks
in the setupTests.js
script they provide (adding setupFilesAfterEnv
directly to my package.json caused an error telling me to add to this file instead, as react-scripts loads their setup file automatically).
Am I missing something? Or is there a problem with dontMockByDefault
and react-scripts?
We're currently using this project with both shallow
and mount
renders. In some tests we use beforeAll
with the render function:
const renderComponent = () => {
component = mount(<LinkEditor {...props} />);
};
beforeEach(renderComponent);
The issue here is that beforeAll
is always executed before beforeEach
. And the mocks are setup in a beforeEach
call.
Changing the beforeEach
in the library to beforeAll
fixes this. But this seems not the best solution, as this might break things.
Would it be possible to also add a beforeAll to mock the methods? Or let the user specify which before-method they want to trigger the mocks on?
Thanks for this package. I'm currently trying to get this to work with an async test, but that doesn't seem to be supported yet. Could this be added?
Example usage:
it('does something async', async () => {
await withHooks(async () => {
const wrapper = shallow(<App />);
await something();
});
});
I'm currently using this as a workaround:
export default function withHooksAsync(test: () => Promise<void>): Promise<void> {
return new Promise<void>((resolve, reject) => {
withHooks(() => {
test().then(resolve).catch(reject);
});
});
}
useRef()
also doesn't work with shallow rendering if the ref.current
value is handled by passing the ref to a DOM node or a component with ref forwarding. In this case, the ref will always be undefined
with shallow rendering.
This can be worked around by mocking out useRef()
and making it return a mock value that doesn't crash the component that's being tested. However, a mock list this:
jest.mock('react', () => ({
...jest.requireActual('react'),
useRef: arg => (typeof arg === 'undefined' ? { current: { getBoundingClientRect: () => ({ left: 0 }) } } : {current: arg}),
}));
will override jest-react-hooks-shallow's mock useEffect()
and revert it to React's default implementation.
I've been able to work around the issue like this:
import React, {useEffect} from 'react';
import mockUseEffect from 'jest-react-hooks-shallow/lib/mock-use-effect/mock-use-effect';
jest.mock('react', () => ({
...jest.requireActual('react'),
useEffect: jest.fn(),
useRef: arg => (typeof arg === 'undefined' ? { current: { getBoundingClientRect: () => ({ left: 0 }) } } : {current: arg}),
}));
then
useEffect.mockImplementation(mockUseEffect());
instead of withHooks()
in every test case where withHooks()
would normally be used.
It would be nice if the library would offer a more elegant solution for this, either by mocking out useRef()
as well or re-exporting mock-use-effect
so that we don't have to have an ugly import like above.
When I test with mount It show "TypeError: Cannot read property '0' of undefined"
I looks length of previousDependencies and length of currentDependencies can be different.
For example,
if i have 2 unit tests which first one uses shallow, second one uses mount,
It throws TypeError: Cannot read property '0' of undefined
const noDepsOrDifferent = (previousDependencies: unknown[], currentDependencies: unknown[]): boolean => {
return previousDependencies === undefined ||
previousDependencies.some((prevDep, index) => prevDep !== currentDependencies[index]);
}
I looks
I am seeing this error:
ReferenceError: beforeEach is not defined
at Object.mockUseEffect [as default] (node_modules/jest-react-hooks-shallow/lib/mock-use-effect/mock-use-effect.js:10:5)
at enableHooks (node_modules/jest-react-hooks-shallow/lib/enable-hooks.js:17:67)
at Object.<anonymous> (config/jest/setup.js:24:39)
import Adapter from 'enzyme-adapter-react-16'; // eslint-disable-line import/no-extraneous-dependencies
import jestFetchMock from 'jest-fetch-mock'; // eslint-disable-line import/no-extraneous-dependencies
import 'babel-polyfill'; // eslint-disable-line import/no-extraneous-dependencies
import enableHooks from 'jest-react-hooks-shallow';
global.fetch = jestFetchMock;
global.local = {
baseurl: "ThisisiAbaseurl"
}
// pass an instance of jest to `enableHooks()`
enableHooks(jest);
Enzyme.configure({ adapter: new Adapter() });
test('Renders default message and updates it on clicking a button', () => {
const component = shallow(<GenerateSecretForm />);
});
Currently, this library doesn't support react 18, is it possible to address this? Thanks in advance
Say you have the following code:
useEffect(() => {
dispatch(fetchData());
return () => {
dispatch(resetPaging());
};
}, [dispatch]);
Does this package allow you to test if resetPaging
was called?
The cleanup functions are only called before invoking the same effect again.
Thank you for this library! can you please demonstrate "cleanup functions are only called before invoking the same effect again." I've gone through the demo but was not able to achieve it. Below is the sample useEffect I've in my project
useEffect(() => {
dispatch({
type: SET_RESOLUTION_STATUS_COUNT_BAR_CHART_FETCHING_DATA,
payload: true,
});
dispatch(getResolutionStatusCount([]));
return () => {
const barChartPayload = {
barChartData: [],
barChartCategories: [],
resolution_status_environment: [],
};
dispatch({
type: SET_RESOLUTION_STATUS_COUNT_BAR_CHART,
payload: barChartPayload,
});
};
}, []);
and Below is the test cases what I have
const setup = (props = {}) => {
return shallow(<ResolutionCount {...props} />)
}
test("dispatch actions on unmount ", () => {
mockState({
generalReducer: {
dateRangePicker: {
startDate: "",
endDate: "",
}
}
});
setup({ componentUsedIn: "range picker" });
expect(mockDispatch).toHaveBeenNthCalledWith(1, {
type: "SET_RESOLUTION_STATUS_COUNT_BAR_CHART_FETCHING_DATA",
payload: true
});
expect(mockDispatch).toHaveBeenNthCalledWith(2, expect.any(Function))
Could you please tell me how would I achieve component unmount here.
Thank you
When running with Jest 26.6.0 (with Create-React-App v4), all of my tests now fail with the error 'Hooks cannot be defined inside tests. Hook of type "beforeEach" is nested within "Test Name"'. This was mentioned as one of the issues in this previous issue , of which the fix was to upgrade to v1.4.2 of jest-react-hooks-shallow. This, however, has not fixed it. Is there some other configuration that is required to make it work with Jest 26.6.0?
Stacktrace:
Error: Hooks cannot be defined inside tests. Hook of type "beforeEach" is nested within "toggles tigonMode and saves it in sessionStore".
at eventHandler (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/eventHandler.js:105:11)
at dispatchSync (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/state.js:65:5)
at _addHook (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/index.js:125:27)
at beforeEach (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/index.js:135:3)
at Object.mockUseEffect [as default] (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-react-hooks-shallow/src/mock-use-effect/mock-use-effect.ts:14:3)
at withHooks (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-react-hooks-shallow/src/enable-hooks.ts:47:49)
at Object.<anonymous> (/Users/robwaddell/src/git/string-dashboard/client/src/app/App.test.jsx:54:7)
at Promise.then.completed (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/utils.js:276:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/utils.js:216:10)
at _callCircusTest (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:212:40)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at _runTest (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:149:3)
at _runTestsForDescribeBlock (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:63:9)
at _runTestsForDescribeBlock (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:57:9)
at _runTestsForDescribeBlock (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:57:9)
at run (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/run.js:25:3)
at runAndTransformResultsToJestFormat (/Users/robwaddell/src/git/string-dashboard/client/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:176:21)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.