Function component method spyOn failing unittest - reactjs

I am using function component where is there is a arrow function.
const handleOnConfig = (cfg) => {
const layout = { ...config };
setConfig(layout);
};
Now, I want to write unit test for that function.
So, I did following in my test file. I used
beforeEach(() => {
let props = {user:user}
view = shallow(<Views {...props} />).instance();
});
test('should call config change call back', () => {
const handleOnConfig = jest.spyOn(view,'handleOnConfig').mockImplementation(() => config);
expect(handleOnConfig).toHaveBeenCalledTimes(1);
});
But, this test case gives error:
TypeError: Cannot read property 'handleOnConfig' of null
Any help would greatly appreciated
Edit:
Views component
const Views = forwardRef(({ user, id }, ref) => {
useEffect(() => {
handleOnConfig();
}, []);
return (
<div>
<Component
user={user}
id={id}
name='Component'
/>
</div>
);
})
export default Views;

Not a problem with spyOn() directly: your view variable is null.
Try using .dive() in the return from shadow():
beforeEach(() => {
let props = {user:user}
view = shallow(<Views {...props} />).dive().instance(); // <-- added .dive()
});

Related

forwardRef with typescript React

I am trying to use the forwardRef in typescript React to access a child state. I've followed examples online but many are for javascript. I'm getting compiler errors with this.
This is the simplest version I can come up with and the compiler doesn't like the console log. It says ref.current may be undefined, so I've used some && logic but now it says getMyState() does not exist on type 'never'. I've also looked up that error as I've seen it before but don't understand it in this context.
const Parent = () => {
const ref = useRef();
console.log(ref.current && ref.current.getMyState());
return(
<>
<Child ref={ref}/>
</>
);
}
const Child = forwardRef((props, ref) => {
const [item, setItem] = useState('hello');
useImperativeHandle(ref, () => ({getMyState: () => {return item}}), [item]);
return (
<>
bob
</>
);
})
Note: I've read the React docs, my use case is a valid one for forwardRef.
Why am I getting this error and how do I extract the state from the child?
You need to define type for the ref with the form of functions you expose in the Child component.
Try like below.
type ChildHandle = {
getMyState: () => string | null;
};
const Child = forwardRef((props, ref) => {
const [item, setItem] = useState("hello");
useImperativeHandle(
ref,
() => ({
getMyState: () => {
return item;
}
}),
[item]
);
return <>bob</>;
});
const Parent = () => {
const ref = useRef<ChildHandle>();
console.log(ref.current && ref.current.getMyState());
return (
<>
<Child ref={ref} />
</>
);
};
Example of using the handle of Child within Parent

Jest: Cannot read property then of undefined

I have situation in my unit test case for a react application, where in a function calls for another function received in props from parent component. The parent component functions definition is something like this:
onSavePropClick(action) {
const save = this.saveProperty(action);
if(action === SAVE){
return () => new Promise(() => {
resolve(this.calculate().then(save));
});
}
return save;
}
This function call has been passed as props to the child component as
<MyComponent finalSave={this.onSavePropClick(SAVE)} onClose={()=>this.setState({closeWindow: true})} />
MyComponent has a function:
savingAndShowResults() {
const { finalSave, onClose } = this.props;
finalSave().then(() => {
onClose();
});
return true;
}
Now when I have a test for the executed, it throws me error as “Cannot read property then of undefined”, the test is as follows
const initialProps={
finalSave: jest.fn(),
onClose: jest.fn()
};
it(‘should handle saving and show results’, () => {
const component = shallow(
<MyComponent {...initialProps} />
);
component.instance().savingAndShowResults();
expect(initialProps.finalSave).toHaveBeenCalled();
expect(initialProps.onClose).toHaveBeenCalled();
});
I am not able to figure out why even on resolving in return in promise of Parent component’s function, gives me this error.
Please suggest.
Assuming initialProps.finalSave is a mock function, you need to make sure you're returning a promise from initialProps.finalSave:
const initialProps = {
finalSave: jest.fn().mockImplementation(() => Promise.resolve());
...
};

How to trigger rerender on test component?

I am testing this connected component:
export class ExclusiveSelectboxesFormSectionView extends React.Component<Props> {
handleSelection = (field: Field, message: string) => {
this.props.setCard({ message, field });
};
cancelSelection = () => this.props.setCard({ message: null, field: null });
render() {
return (
<View>
{this.props.cancelTitle && (
<CheckBox
title={this.props.cancelTitle}
checked={this.props.card.field == null}
onPress={this.cancelSelection.bind(this)}
checkedIcon="dot-circle-o"
uncheckedIcon="circle-o"
/>
)}
{this.props.fields.map((field, i) => {
const props = {};
props.isSelected = this.props.card.field == field;
props.selectionHandler = this.handleSelection.bind(this);
return <ExclusiveFieldView field={field} key={i} {...props} />;
})}
</View>
);
}
}
const mapState = ({ currentFormReducer }) => {
const card = currentFormReducer.card || { message: null, field: null };
return { card };
};
const mapDispatch = { setCard };
export default connect(mapState, mapDispatch)(ExclusiveSelectboxesFormSectionView);
I'm trying to test the non-connected component using react-native-testing-library. The component works in the app, but this test is failing to find "Second field option 2" in the next-to-last assertion in the test.
// non-connected component
import { ExclusiveSelectboxesFormSectionView } from "../../src/components/ExclusiveSelectboxesFormSectionView";
function createWrapper(customProps) {
let mockCard = { message: null, field: null };
const props = {
fields,
setCard: jest.fn().mockImplementation((card: Types.Card) => {
mockCard = card;
}),
card: mockCard,
...customProps
};
wrapper = render(
<Fragment>
<ExclusiveSelectboxesFormSectionView fields={fields} {...props} />
</Fragment>
);
return wrapper;
}
describe("ExclusiveSelectboxesFormSectionView", () => {
let checkboxes;
beforeEach(() => {
wrapper = createWrapper();
checkboxes = wrapper.getAllByType(CheckBox);
expect(checkboxes.length).toBe(3);
});
fit("shows the value of the currently selected field", async () => {
await fireEvent.press(checkboxes[1]); // show options
await fireEvent.press(wrapper.getByText("Second field option 2")); // select option
const component = wrapper.getByType(ExclusiveSelectboxesFormSectionView);
expect(component.props.setCard).toHaveBeenCalled();
// options should be gone
expect(wrapper.queryByText("Second field option 1")).toBeNull();
// selected option should still be on screen
expect(wrapper.getByText("Second field option 2")).toBeDefined();
expect(checkboxes[1].props.checked).toBe(true);
});
});
I've passed in a card prop and a setCard mock function prop, in place of redux providing these.
The mock setCard function is being called, so I think the problem is that the component is not rerendering with its new props (and a newly set card prop). A log statement in the component's render function confirms this (it only prints once when the test is run).
I imagine there's something basic I'm missing about how I'm rendering the component, or wrapping it, or calling it, or something.
Can anyone spot my problem?
It looks like react-native-testing-library's update function does the trick. But it was a bit tough to figure out.
// refactored from createWrapper
function getWrapperProps() {
return {
fields,
setCard: jest.fn().mockImplementation((card: Types.Card) => {
mockCard = card;
}),
card: mockCard
};
}
function createWrapper(customProps) {
wrapper = render(
<Fragment>
<ExclusiveSelectboxesFormSectionView
{...getWrapperProps()}
{...customProps}
/>
</Fragment>
);
return wrapper;
}
function updateWrapper(customProps) {
wrapper.update(
<Fragment>
<ExclusiveSelectboxesFormSectionView
{...getWrapperProps()}
{...customProps}
/>
</Fragment>
);
checkboxes = wrapper.getAllByType(CheckBox);
}
// call updateWrapper() when you need to get the newly rendered props
it("shows the value of the currently selected field", async () => {
await fireEvent.press(checkboxes[1]);
await fireEvent.press(wrapper.getByText("Second field option 2"));
const component = wrapper.getByType(ExclusiveSelectboxesFormSectionView);
expect(component.props.setCard).toHaveBeenCalled();
updateWrapper();
// options should be gone
expect(wrapper.queryByText("Second field option 1")).toBeNull();
// selected option should still be on screen
expect(wrapper.getByText("Second field option 2")).toBeDefined();
expect(checkboxes[1].props.checked).toBe(true);
});
Now it passes.
I'd love to know of other options. I'm not clear why this had been working with an earlier implementation, although the earlier implementation used state in the component, which I'm sure is basically the answer.

How to verify method is not invoked on mock passed as prop

I am developing a React application with jest and TypeMoq.
I can't test the negative path of a decision tree when the mocked call is a method on the object which needs to be undefined. Is there a method on TypeMoq that can help me verify that the provided method is not called?
type TopicComponentProps = {
topic: Topic
history?: History<any>
}
export const TopicComponent = ({topic, history} : TopicComponentProps) => {
const { Id, Name } = topic;
const filterTopic = () => {
if (history) { // <-- this is my problem
history.push(`/topic/overview/${Id}`);
}
}
return(
<Fragment>
<span
style={topicStyle}
onClick={() => filterTopic()}
className="topic">
{Name}
</span>
</Fragment>
)
}
The positive test case looks like this:
it('should trigger the navigation when clicked', () => {
const mockHistory = Mock.ofType<History<any>>();
const wrapper = mount(
<TopicComponent topic={testTopic} history={mockHistory.object} />
);
wrapper.simulate('click');
mockHistory.verify(x => x.push(It.isAnyString()), Times.once());
});
How do I setup the mock object, so i can test that no navigation happens when no history is provided?
it('should not trigger the navigation when history is undefined', () => {
let mockHistory = Mock.ofType<History<any>>();
???
const wrapper = mount(
<TopicComponent topic={testTopic} history={???} />
);
wrapper.simulate('click');
mockHistory.verify(x => x.push(It.isAnyString()), Times.never());
});

|ReactNative|-Conditional rendering ?: with Jest && Enzyme

Good afternoon,
I have a component file structured like that globally :
class Component ...
render(){
const {array} = this.props
{!array.includes(value) ?
(<View ...props
id="myComponent"/>
....
</View>) :
(<View ...props
id="myOtherComponent"/>
....
</View>)
}
}
And in my test file, i'm doing the stuff like that :
describe('Testing Component', () => {
test('conditional rendering', () => {
const wrapper = shallow(<Component array={[value]}/>);
expect(wrapper.find(n => n.prop('id') === "myOtherComponent").exists(true))
});
});
But even if I modify the props sent for the array, it always returned me true... What's the keyword to check that the nested component is actually verified and rendered...
I think the error is in your expect argument.
I would use the findWhere function instead of find;
The exists method should not receive a parameter in this
case, as it only receives Enzyme's Selectors and not booleans (you can read more about it here);
Add a toBeTruthy call to the expect line.
Here's a similar situation to yours that we have a test for and it works just fine:
it('tests name', () => {
const mockComponent = shallow(<Component {...props} />);
const textNode = mockComponent.findWhere(n => n.text() === props.name);
expect(textNode.exists()).toBeTruthy();
});
So your test would end up looking like this:
describe('Testing Component', () => {
test('conditional rendering', () => {
const wrapper = shallow(<Component array={[value]}/>);
const node = wrapper.findWhere(n => n.prop('id') === 'myOtherComponent');
expect(node.exists()).toBeTruthy();
});
});

Resources