Reputation: 692
I'm working on a unit test in a React application that verifies a passed in prop function is being conditionally called based on another props value. I'm utilizing Typescript/Enzyme/Jest in my application and am using a Root
wrapper around the component I'm testing to inject the Redux store (and override initial state if desired).
import { mount, ReactWrapper } from "enzyme";
import React from "react";
import Login from "src/components/auth/Login";
import Root from "src/Root";
let wrapped: ReactWrapper;
let defaultProps = {
signIn: jest.fn(),
token: null,
};
beforeEach(() => {
wrapped = mount(
<Root>
<Login {...defaultProps} />
</Root>
);
});
describe("on mount", () => {
describe("if no token is supplied to the props", () => {
it("will call the props.signIn() function", () => {
expect(defaultProps.signIn).toHaveBeenCalled();
});
});
});
When I run the test the toHaveBeenCalled()
(as well as toBeCalled()
, tried both) are not registering any calls. However, I have supplied a console.log
statement that is getting triggered within the same conditional that the signIn()
function lives.
import React from 'react';
import { AuthState, JWT } from "src/components/auth/types";
import { signIn } from "src/redux/auth";
interface Props {
signIn: () => Promise<void>;
token: null | JWT;
}
class Login extends React.Component<Props> {
/**
* Sign the user in on mount
*/
public componentDidMount(): void {
if (!this.props.token) {
console.log("GETTING HERE");
this.props.signIn();
}
}
public render(): JSX.Elemeent {
// ... More code
}
}
const mapStateToProps = (state: AuthState) => {
return {
token: state.auth.token;
};
};
export default connect(mapStateToProps, { signIn })(Login);
I've gone through several related posts/articles but all of the different configurations, such as traversing enzyme to get the direct prop or utilizing spyOn
, have failed.
The only thing I can figure that's different is my wrapping of the Login
component with Root
, but considering I can see the console.log
being triggered this seems like a complete shot in the dark.
Can anyone tell me what I'm doing wrong here?
Upvotes: 0
Views: 1420
Reputation: 692
Ended up being me forgetting to place the override via mapDispatchToProps
and mapStateToProps
in the connect
function. This was causing my passed in signIn
function to be overridden by the signIn
action imported in the file. Updating with ownProps
and conditionally utilizing the passed in value fixes the issue:
const mapStateToProps = (state: AuthState, ownProps: Props) => {
return {
token: ownProps.token || state.auth.token;
};
};
const mapDispatchToProps = (dispatch: ThunkDispatch<{}, {}, any>, ownProps: Props) => {
return {
signIn: ownProps.signIn || (() => { return dispatch(signIn()) })
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Upvotes: 0
Reputation: 2648
You've to wait for component to mount, so:
it("will call the props.signIn() function", (done) => {
setImmediate(() => {
expect(defaultProps.signIn).toHaveBeenCalled();
done()
});
});
Upvotes: 1