How use toHaveBeenCalled() inside FileReader.onload - reactjs

I have component that load image and send image data up to parent component via props.handle function
How I can mock or call props.handle inside fileReader.onload
Maybe need use async, but I don't know how.
Was try repeat code from this question How do I test `image.onload` using jest in the context of redux actions (or other callbacks assigned in the action)
But it didn't help
ChildComponent.js:
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
}
handleFileLoad = event => {
event.preventDefault();
const reader = new FileReader();
reader.onload = () => {
const data = reader.result;
this.props.parentHandleFunction(data)
}
reader.readAsDataURL(event.target.files[0]);
}
}
ChildComponent.propTypes = {
parentHandleFunction: PropTypes.func.isRequired,
};
ChildComponent.test.js:
describe('<ChildComponent />', () => {
let renderComponent;
let changeSpy;
beforeEach(() => {
changeSpy = jest.fn(value => value);
renderComponent = shallow(
<ChildComponent parentHandleFunction={changeSpy}/>,
);
});
it('should call handle file change', () => {
const childComponent = shallow(
renderComponent.find(ChildComponent).getElement(),
);
const file = new Blob(['image'], { type: 'image/jpeg' });
loadButton.find('input').simulate('change', {
preventDefault: () => {},
target: {
files: [file],
},
});
expect(changeSpy).toHaveBeenCalled();
});
})
Tests show errror:
'Expected mock function to have been called, but it was not called.'
update
I solve my problem by cuting onload logic in seprate function
## ChildComponent.js ##
class ChildComponent extends React.PureComponent {
constructor(props) {
super(props);
}
loadImage = data => {
const imageObject = {
url: data,
};
this.props.parentHandleFunction(
imageObject,
);
}
handleFileLoad = event => {
event.preventDefault();
const reader = new FileReader();
reader.onload = async () => this.loadImage(reader.result);
reader.readAsDataURL(event.target.files[0]);
}
}
ChildComponent.propTypes = {
parentHandleFunction: PropTypes.func.isRequired,
};
ChildComponent.test.js:
it('should call change spy function', () => {
renderComponent.instance().loadImage('mockImage');
renderComponent.update();
renderComponent.instance().forceUpdate();
expect(changeSpy).toHaveBeenCalled();
});

Related

How to convert class component to function component with hooks

my code class component
raf = (callback) => {
var self = this;
if (this.requestFrame) raf.cancel(this.requestFrame);
this.requestFrame = raf(function () {
self.requestFrame = undefined;
callback();
});
}
update = (callback) => {
this.raf(function () { this._update(callback) });
}
my code function component
const raf = (callback) => {
if (Scrollbars.requestFrame) raf.cancel(Scrollbars.requestFrame);
Scrollbars.requestFrame = raf(() => {
Scrollbars.requestFrame = undefined;
callback();
});
};
const update = (callback) => {
raf(() => _update(callback));
};
I am switching from class component to function component but when i switch I get error "Maximum call stack size exceeded" in function raf.
Someone please help me.
In your function component add below code and try:
const update = (callback) => {
useEffect(() => {
raf(() => _update(callback));
}, []);
};

Unit Testing with Mocha,Enzyme dispatched functions in functional components which uses React Hooks + Redux

I am trying to test a dispatch from 'mapDispatchToProps' defined with a functional component which uses useEffect() hook and the function is called there.
export const MyComponent = (props) => {
useEffect(() => {
// Anything in here is fired on component mount.
props.registerInStore(props.id, false);
return () => {
// Anything in here is fired on component unmount.
props.resetInStore(props.id);
};
}, []);
const handleOnClick = () => {
props.toggle(props.id);
};
return (
<div >
{!props.isOpen ? (
<button
onClick={handleOnClick}>
Open
</button>
) : (
<button
onClick={handleOnClick}>
close
</button>
)}
</div>
);
};
const mapDispatchToProps = (dispatch) => ({
registerInStore(id, isOpen) {
dispatch(registerInStore(id, isOpen));
},
resetInStore(id) {
dispatch(resetInStore(id));
}
});
export default connect(null, mapDispatchToProps)(MyComponent);
In my unit tests with Mocha and enzyme i also want to test the dispatches inside 'mapDispatchToProps', what i did below does not seem to work :
describe('<MyComponent/>', () => {
let store = mockStore({
toggles: [
{
id: 10,
isOpen: true
}
]
}
});
const options = {
context: {store},
childContextTypes: {store: PropTypes.object.isRequired},
lifecycleExperimental: true
};
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore: expect.createSpy(),
resetInStore: expect.createSpy(),
toggle: expect.createSpy(),
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};
afterEach(() => {
expect.restoreSpies();
});
it('should dispatch', async () => {
const {wrapper}=setup();
await store.dispatch(wrapper.prop('registerInStore')(10,false));
/* i tried the commented way too instead of directly dispatching*/
// wrapper.prop('registerInStore')(10,false);
//await new Promise((resolve) => setTimeout(resolve, 50));
const expectedActions = [{type: 'REGISTER_IN_STORE', id: 10, isOpen: false}];
expect(store.getActions()).toEqual(expectedActions);
});
the store.getActions() is returning an empty array, i am new to React Hooks and testing, what am i doing wrong, any other solutions?.
Thanks in Advance.
worked by removing the spies e.g:-
const setup = (inputProps = {}) => {
const props = {
id: 10,
isOpen: false,
registerInStore:()=>null,
resetInStore: ()=>null,
toggle: ()=>null,
...inputProps
};
const wrapper = mount(<MyComponent {...props} />, options);
return {
props,
wrapper
};
};

React Native: Cannot update a component from inside the function body of a different component

I have a very simple react native screen which looks as follows:
class BasicScreen extends React.Component {
state = {
data: [],
myItems: [],
};
componentDidMount() {
this.checkforItems();
}
checkforItems = async () => {
AsyncStorage.getItem('MyItems').then(item => {
if (item) {
this.setState({
myItems: JSON.parse(item),
});
} else {
console.log('No data.');
}
});
};
render() {
return (
<View>/* A detailed view */</View>
)
}
}
The problem is that it causes the following error:
Cannot update a component from inside the function body of a different component.
I'm not sure how to fix this. Please help.
This looks like it is just going to overwrite myItems with whatever the most recent mapped item is rather than appending each new item.
You might try mapping the items to an array in componentDidMount() and then set the state in a useEffect call.
const BasicScreen = () => {
const [myData, setData] = useState([]);
const [myItems, setItems] = useState([]);
const checkForItems = () => {
var storageItems = AsyncStorage.getItem("MyItems").then((item) => {
if (item) {
return JSON.parse(item);
}
});
setItems(storageItems);
};
useEffect(() => {
async function getItems() {
await checkForItems();
}
getItems();
}, []);
return (
<View>
<Text>{myItems[0]}</Text>
</View>
);
};
export default BasicScreen;

Not working tests for moct store Mocha Enzyme

I am trying to test if a button dispatches an action but instead of an action type I get []. The functionality works completely fine outside the test this is why I don't understand why the test fails.
My test:
const mockStore = configureStore();
const store = mockStore({});
describe('buttonUploadWorks', () => {
it('checks if the button for upload dispatches an action', () => {
const wrapper = shallow(
<Provider store = {store}>
<DropzoneComponent.WrappedComponent/>
</Provider>).dive();
const uploadButton = wrapper.find('button').at(0);
uploadButton.simulate('onClickUpload');
const expectedAction = UPLOAD_STARTING;
wrapper.setState({ accepted: ['abc'] });
const action = store.getActions();
expect(action).to.equal(expectedAction);
});
});
My actions:
export const uploadStarting = () => {
return {type: UPLOAD_STARTING};
};
export const uploadSuccess = uploadMessage => {
return {type: UPLOAD_SUCCESS, uploadMessage};
};
export const uploadFail = error => {
return {type: UPLOAD_FAIL, error};
};
export function tryUpload (file) {
const formData = new FormData();
formData.append('file', file);
return (dispatch) => {
dispatch(uploadStarting());
axios.post(filesEndpoint, formData).then(function (response) {
dispatch(uploadSuccess(response));
}).catch(function (error) {
dispatch(uploadFail(error));
});
};
};
And the button:
<button className='buttonUpload' onClick={() => { this.state.accepted.length > 0 ? this.onClickUpload().bind(this) : alert('No files presented'); }}>UPLOAD</button>
onClickUpload() {
this.props.dispatch(tryUpload(this.state.accepted[0]));
localStorage.clear();
this.setState({accepted: []});
this.props.history.push(searchPath);
}
That's happening because setState({accepted:[]}) triggers before this.props.dispatch(tryUpload(this.state.accepted[0])) you could fix it binding dispatch function inside a promise function and then calling the setState function.
JavaScript Promises

jest enzyme testing a component with async fetch to api

I'm using react, jest and enzyme with immutable. I'm trying to mount a component that fetching data from the API, and I'm having a little difficulties.
// FooListContainer.jsx
export default class FooListContainer extends Component {
constructor(props) {
super(props);
this.state = {
foos: List()
}
}
componetWillMount() {
manager.bringFooList()
.then(lst => this.setState({ foos: fromJS(lst) }))
.done();
}
render() {
return <FooList foos={this.state.foos} />
}
}
This is the ui component all it does is receive list and map them
// FooList.jsx
export default class FooList extends Component {
render() {
return (
<div>
{this.props.foos.map(item => <div>{item}</div>)}
</div>
);
}
}
Now I would like to test the data received from the fetch in FooListContainter is passed correctly to FooList.
// FooListContainer.test.jsx
describe('rendering', () => {
it('Should passed the data from the fetch to the FooList', () => {
const response = ['1', '2', '3'];
manager.bringFooList = jest.fn(() => {
return new Promise(resolve => {
return resolve(response);
});
})
const wrapper = mount(<FooListContainer />);
const fooList = wrapper.find(FooList);
expect(fooList.props().foos.size).toBe(3);
});
});
But the test fails because it expects the length to be 3 and it actual length is 0 from some reason.
I think that it has something to do with the fact that the fetch inside the container is async - so the test is not 'waiting' to the response and render is happening before the state change for the first time and the FooList receive an empty list.
I have tried to receive in the 'it' function an async callback as an argument and call it after the mount, like this:
// FooListContainer.test.jsx
describe('rendering', () => {
it('Should passed the data from the fetch to the FooList', (done) => {
const response = ['1', '2', '3'];
manager.bringFooList = jest.fn(() => {
return new Promise(resolve => {
return resolve(response);
});
})
const wrapper = mount(<FooListContainer />);
done();
const fooList = wrapper.find(FooList);
expect(fooList.props().foos.size).toBe(3);
});
});
But the above example did not work.
I would really appreciate every help you could give me.

Resources