In my React Native app, we recently integrated TypeScript and I'm in charge of migrating the unit tests. One particular test is failing unexpectedly.
The app includes a <LoginForm />
component that utilizes Formik.
//... imports
export interface FormValues {
email: string;
password: string;
}
export interface Props {
navigation: NavigationScreenProp<any, any>;
}
export default class LoginForm extends Component<Props, object> {
handleSubmit = (values: FormValues, formikBag: FormikActions<FormValues>) => {
// ... api calls and stuff
};
renderForm = ({
values,
handleSubmit,
setFieldValue,
touched,
errors,
setFieldTouched,
isValid,
isSubmitting
}: FormikProps<FormValues>) => (
<View style={styles.container}>
// ... two inputs and a button
</View>
);
render() {
return (
<Formik
initialValues={{ email: "", password: "" }}
onSubmit={(values: FormValues, formikBag: FormikActions<FormValues>) =>
this.handleSubmit(values, formikBag)
}
validationSchema={<some_schema>}
render={(formikBag: FormikProps<FormValues>) => this.renderForm(formikBag)}
/>
);
}
}
This is how the unit test looks for the Function as Child renderForm()
:
describe("renderForm", () => {
let formWrapper: ShallowWrapper;
beforeEach(() => {
let formikBagMock: any = {
values: { email: "Test Email", password: "testpassword" },
handleSubmit: jest.fn(),
setFieldValue: jest.fn(),
errors: { email: "Test error", password: "" },
touched: { email: true, password: false },
setFieldTouched: jest.fn(),
isValid: false,
isSubmitting: false
};
formWrapper = shallow(wrapper.instance().renderForm(formikBagMock));
});
it("should render a <View />", () => {
expect(formWrapper.find("View")).toHaveLength(1);
});
it("should render two <Input />", () => {
expect(formWrapper.find("Input")).toHaveLength(2);
});
it("should render a <Button />", () => {
expect(formWrapper.find("Button")).toHaveLength(1);
});
describe("styling", () => {
it("should give the <View /> the 'container' style", () => {
expect(formWrapper.find(View).prop("style")).toEqual(styles.container);
});
});
});
The issue arises when this test passes in .js
but fails in .tsx
.
The error thrown displays:
● LoginForm › rendering › renderForm › should render a <Button />
expect(received).toHaveLength(length)
Expected value to have length:
1
Received:
{Symbol(enzyme.__root__): {Symbol(enzyme.__root__): [Circular], Symbol(enzyme.__unrendered__): <Component style={{"alignItems": "center", "flex": 1, "justifyContent": "space-evenly"}}><Input autoCapitalize="none" editable={true} errorMessage="Test error" keyboardType="email-address" onBlur={[Function onBlur]} onChangeText={[Function onChangeText]} placeholder="Email address" value="Test Email" /><Input autoCapitalize="none" editable={true} onBlur={[Function onBlur]} onChangeText={[Function onChangeText]} placeholder="Password" secureTextEntry={true} value="testpassword" /><Button TouchableComponent={[Function anonymous]} buttonStyle={{"backgroundColor": "#DC4F19"}} clear={false} containerStyle={{"paddingVertical": 5, "width": "33%"}} disabled={true} disabledStyle={{"backgroundColor": "#DC4F19", "opacity": 0.3}} disabledTitleStyle={{"color": "white"}} iconRight={false} loading={false} loadingProps={{"color": "white", "size": "large"}} onPress={[Function mockConstructor]} raised={false} title="Log In" titleStyle={{"color": "white"}} /></Component>, Symbol(enzyme.__renderer__): {"batchedUpdates": [Function batchedUpdates], "getNode": [Function getNode], "render": [Function render], "simulateError": [Function simulateError], "simulateEvent": [Function simulateEvent], "unmount": [Function unmount]}, Symbol(enzyme.__node__): {"instance": null, "key": undefined, "nodeType": "function", "props": {"children": [Function anonymous]}, "ref": null, "rendered": [Function anonymous], "type": {"$$typeof": Symbol(react.context), "Consumer": [Circular], "Provider": {"$$typeof": Symbol(react.provider), "_context": [Circular]}, "_calculateChangedBits": null, "_currentRenderer": null, "_currentRenderer2": null, "_currentValue": false, "_currentValue2": false, "unstable_read": [Function bound readContext]}}, Symbol(enzyme.__nodes__): [{"instance": null, "key": undefined, "nodeType": "function", "props": {"children": [Function anonymous]}, "ref": null, "rendered": [Function anonymous], "type": {"$$typeof": Symbol(react.context), "Consumer": [Circular], "Provider": {"$$typeof": Symbol(react.provider), "_context": [Circular]}, "_calculateChangedBits": null, "_currentRenderer": null, "_currentRenderer2": null, "_currentValue": false, "_currentValue2": false, "unstable_read": [Function bound readContext]}}], Symbol(enzyme.__options__): {"adapter": {"options": {"enableComponentDidUpdateOnSetState": true, "lifecycles": {"componentDidUpdate": {"onSetState": true}, "getDerivedStateFromProps": true, "getSnapshotBeforeUpdate": true, "setState": {"skipsComponentDidUpdateOnNullish": true}}}}, "attachTo": undefined, "hydrateIn": undefined}}, Symbol(enzyme.__unrendered__): null, Symbol(enzyme.__renderer__): {"batchedUpdates": [Function batchedUpdates], "getNode": [Function getNode], "render": [Function render], "simulateError": [Function simulateError], "simulateEvent": [Function simulateEvent], "unmount": [Function unmount]}, Symbol(enzyme.__node__): undefined, Symbol(enzyme.__nodes__): [], Symbol(enzyme.__options__): {"adapter": {"options": {"enableComponentDidUpdateOnSetState": true, "lifecycles": {"componentDidUpdate": {"onSetState": true}, "getDerivedStateFromProps": true, "getSnapshotBeforeUpdate": true, "setState": {"skipsComponentDidUpdateOnNullish": true}}}}, "attachTo": undefined, "hydrateIn": undefined}}
received.length:
0
61 |
62 | it("should render a <Button />", () => {
> 63 | expect(formWrapper.find("Button")).toHaveLength(1);
| ^
64 | });
65 |
66 | describe("styling", () => {
It's puzzling why Enzyme suddenly can't find the node anymore. I attempted importing the components View
, Button
, and Input
directly and using them with find()
instead of strings, but it had no effect. What could be causing this discrepancy? Any ideas?