Test callback prop with Enzyme - jestjs

I have React-Spring animation in my component:
<SpinnerKf state={status} onRest={changeView && status === 'SUCCESS' ? () => changeView(VIEW_MODES.RECEIPT) : null}>
....
</SpinnerKf>
Where I pass function call inside onRest prop - this is the prop from React-Spring Keyframe, which is called after animation end.
How can I cover this with a test? I'm opened for any tricks, just need to avoid complaining in test coverage.

You can use Enzyme to get the SpinnerKf component and then call its onRest property directly.
Here is a simplified example:
code.js
import * as React from 'react';
const SpinnerKf = () => null;
export const Component = () => (<SpinnerKf onRest={() => { return 'does something'; }}/>);
code.test.js
import * as React from 'react';
import { shallow } from 'enzyme';
import { Component } from './code';
test('callback', () => {
const wrapper = shallow(<Component />);
const result = wrapper.find('SpinnerKf').props().onRest();
expect(result).toBe('does something'); // Success!
});
Note that testing the return value or behavior of the callback is optional, as long as it runs during a unit test it will be included in the code coverage report.

Related

React "The top-level-await experiment is not enabled" and "await"

I need to await some data from another file but whenever I try to run following code, an error comes up.
My Code:
import React from 'react';
import './App.css';
import { getApiData } from './functions/player-api-data'
export async function App() {
const data = await (getApiData('a3d7dc03c0e84c3eaa726110df90cbf8', 'player') as any)
console.log(await data)
return (
<div className="App">
<pre>{JSON.stringify(await data)}</pre>
</div>
);
}
export default await App as any
The Error:
Module parse failed: The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enabled it)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/babel-loader/lib/index.js
* ./node_modules/source-map-loader/dist/cjs.js
You may need an additional loader to handle the result of these loaders.
Error: The top-level-await experiment is not enabled (set experiments.topLevelAwait: true to enabled it)
I have tried to find the webpack.config.js file but in the existing file, there's no "experiments" object to be found in it. If there is a better way than fixing this specific error that would be great too.
React Components Cannot be Asynchronous. If you want asyncronous data, you can use UseEffect. Modify your code as..
import React,{useEffect} from 'react';
import './App.css';
import { getApiData } from './functions/player-api-data'
function App() {
const [data,setData] = useState();
const getData = async () => {
let result = await getApiData('a3d7dc03c0e84c3eaa726110df90cbf8', 'player')
setData(result)
}
useEffect(() => {
// Fetching Data on Initial Load
getData()
},[])
return (
<div className="App">
// will render if data is primitive or valid jsx
<pre>{data}</pre>
</div>
);
}
export default App

How to use react-intl with jest

I'm working with React Testing Library an I need to get the translation for a text, we mocked in our setupTest file the react-intl library:
jest.mock('react-intl', () => {
const reactIntl = require.requireActual('react-intl');
const intl = reactIntl.createIntl({
locale: 'en'
});
return {
...reactIntl,
useIntl: () => intl
};
});
but I don't know how to use it in the test, could someone provide me an complete examble of a test using the library to get a translation, please?
I tried to do it in this way:
let intl = useIntl();
let i18n = {
header: intl.formatMessage({
id: 'header.myHeader',
defaultMessage: 'header.myHeader'
})
};
but there are any messages in intl from the locales.
Regards.
Don't mock it, try and directly wrap your component with IntlProvider, or even better, create a helper render where you wrap with IntlProvider and render it with that:
import {IntlProvider} from 'react-intl`;
import {render} from '#testing-library/react';
const renderWithReactIntl = (component, locale, messages) => {
return render(<IntlProvider locale={locale} messages={messages}>
{component}
</IntlProvider>
);
};
Within your test() or it() just wrap your unconnected component in renderWithReactIntl:
const { /testing library selector goes here/ }
= renderWithReactIntl(<YourComponent />, messages, locale)

Jest spiedOn method is not called, but called once change the test case order

The Spied on code is not called when the test is at the second position. (or a second render) and test case fails
Test case passes if the test case is at the first position(or on first render).
Using a very basic create-react-app OOTB example and simplifying it even more for a MCVE:
MyModule.js
import React from 'react';
import someClass from './someClass';
function App() {
someClass.track("someevent");
return null;
}
export default App;
someClass.js
class SomeClass {
constructor() {
this.someProp = null;
}
getSatellite() {
return {
track: () => {}
};
}
track(someProp) {
///THIS BELOW IF CLAUSE IS THE PROBLEM
if (this.someProp === someProp) {
return;
} else {
this.someProp = someProp;
}
///////////////////////
this.getSatellite().track('someevent');
}
}
const instance = new SomeClass();
export default instance;
App.js
import React from 'react';
import MyModule from './MyModule'
function App() {
return (
<div className="App">
<MyModule />
</div>
);
}
export default App;
App.test.js
import React from 'react';
import { render } from '#testing-library/react';
import App from './App';
import someClass from './someClass';
test('renders learn react link', () => {
render(<App />);
});
// it works if this test case is first one, weird :-|
test('renders class', () => {
const track = jest.fn();
jest.spyOn(someClass, 'getSatellite').mockImplementation(()=>{
console.log('here i am');
return {
track
}
})
render(<App />);
expect(track).toHaveBeenCalledTimes(1);
});
Output:
✓ renders learn react link (17ms)
✕ renders class (5ms)
● renders class
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
18 | })
19 | render(<App />);
> 20 | expect(track).toHaveBeenCalledTimes(1);
| ^
21 | });
22 |
at Object.<anonymous> (src/App.test.js:20:17)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 10.472s
Let me know if you need anything in the comments.
In the first render(<App />) the someProp of someClass is being set as someevent.
Now in the next render I am only mocking the function call, but not resetting the someProp. That's why the if (this.someProp === someProp) is taking effect.
So I need to reset the someProp to another value or null and it will work fine.

Using react-hooks-testing-library with jest.spyOn - spy is not called

I am having an issue setting up a unit test to determine that a function is called with the correct arguments. useAHook returns function foo which calls function bar. The code looks like this
//myModule.js
export const useAHook = (arg1, arg2) => {
const foo = useCallback(() => {
bar(arg1, arg2);
}, [arg1, arg2]);
return foo;
}
export const bar = (a, b) => {
//does some stuff with a and b
}
I am trying to unit test this code using renderHook and jest.spyOn. I want to confirm that calling function foo results in bar being called with the correct arguments. My unit test looks like this
//myModule.spec.js
import * as myModule from './myModule.js'
it('should call foo with correct arguments', () => {
const spy = jest.spyOn(myModule, 'bar');
const { result } = renderHook(() => myModule.useAHook('blah', 1234));
const useAHookFunc = result.current;
useAHookFunc();
// fails, spy is not called
expect(spy).toBeCalledWith('blah', 1234);
});
The result is that the test fails saying that spy is never called. Am I doing something wrong here or using either tool incorrectly?
This line:
import * as myModule from './myModule.js'
...imports the module bindings for myModule.js into myModule.
Then this line:
const spy = jest.spyOn(myModule, 'bar');
...wraps the module export for bar in a spy...
...but the spy never gets called because useAHook doesn't call the module export for bar, it just calls bar directly.
If you modify useAHook to call the module export for bar then the spy will get called.
There are a couple of ways to do that.
You can move bar into its own module...
...or you can import the module bindings for myModule.js so you can call the module export for bar:
import { useCallback } from 'react';
import * as myModule from './myModule'; // <= import the module bindings
export const useAHook = (arg1, arg2) => {
const foo = useCallback(() => {
myModule.bar(arg1, arg2); // <= call the module export for bar
}, [arg1, arg2]);
return foo;
}
export const bar = (a, b) => {
//does some stuff with a and b
}
import * as myModule from './...'
const mockedFn = spyOn(myModule, "useSomething")
I managed to spy on the hook export method (using import * as), then inject a mock function into the implementation:
import * as useThingHook from 'useThing'
it('a test', () => {
const methodMock = jest.fn()
jest.spyOn(useThingHook, 'usething').mockImplementation(() => ({
method: methodMock
}))
act()
expect(methodMock).toHaveBeenCalled()
})

Mocking node dependency for Typescript with Mocha / Sinon

I have a class which controls an audio receiver using an external library marantz-avr:
let AVReceiver = require('marantz-avr');
class MarantzPlugin {
hardwareInstance;
constructor(pluginConfiguration) {
const receiver = new AVReceiver(pluginConfiguration.settings.ip);
this.hardwareInstance = receiver;
}
turnPowerOn() {
this.hardwareInstance.setPowerState('on').then(res => res, error => console.error(error));
}
}
export default MarantzPlugin;
I want to unit test this class. In order to do so I have to mock the marantz-avr library since this library only works when an actual receiver is found on the provided ip address.
In the test below I mock the marantz-avr, however the MarantzPlugin still uses the original AVReceiver instead of the mocked one.
import { suite, test, slow, timeout } from 'mocha-typescript';
import * as mocha from 'mocha';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as should from 'should';
import MarantzPlugin from './';
let AVReceiver = require('marantz-avr');
#suite
class MarantzPluginTest {
public create() {
before(() => {
sinon.stub(AVReceiver.prototype, 'AVReceiver').callsFake(() => {
return {
setPowerState: () => {
return true;
}
}
});
});
let marantz = new MarantzPlugin({
id: 'MARANTZ',
settings: {
ip: '192.168.178.2',
}
});
marantz.setPowerState('on');
}
}
I've looked into http://sinonjs.org/how-to/link-seams-commonjs/ but when implemented it gave me Error: Cannot find module 'marantz-avr'
Does anyone see what I am missing here, or perhaps have a better way to unit test these kind of classes?

Resources