-
Notifications
You must be signed in to change notification settings - Fork 276
Description
Ask your Question
Hello,
I'm working on a "react-native" app that works with "formik" and "yupjs".
I created a login form that works fine in Android and iOS emulators, as well as on the web via react-native-web.
I test this application with jest and react-native-testing-library for the Android and iOS part.
It's the first time I use react-native-testing-library,
I have a problem when I want to test that an error message is displayed when a user enters a non-compliant email and/or password, on the native part.
This validation is done with formik and yupjs, and is automatically triggered during an onChange event on an input.
However, this event does not trigger and the error message does not appear.
I removed the "isDisabled" props on the submit button so when I fire a press event on the submit button the validation is triggered and all the tests works fine but it's not a solution because the specifications do not allow to submit if the form is invalid.
Tell me if you need more information
BTW I'm not native english speaker, please be indulgent.
Login form
export default function LoginForm() {
const intl = useIntl();
const login = useLoginUser();
const initialValues = {
email: '',
password: '',
};
return (
<Formik
initialValues={initialValues}
validateOnChange
validateOnBlur
validateOnMount
validationSchema={validationSchema(intl)}
onSubmit={(
values: FormikFields,
formikBag: FormikHelpers<FormikFields>,
) => login(values, formikBag)}
>
{({
handleSubmit,
}) => (
<>
<InputText<FormikFields>
name="email"
type="email"
label={intl.formatMessage(translations.emailLabel)}
placeholder={intl.formatMessage(translations.emailLabel)}
/>
<InputPassword<FormikFields>
name="password"
label={intl.formatMessage(translations.passwordLabel)}
/>
<Button
onPress={() => handleSubmit()}
alignSelf="center"
>
{intl.formatMessage(translations.submitButton)}
</Button>
</>
)}
</Formik>
);
}My input text wrapper
export default function InputText<T extends {
[key: string]: string | undefined,
}>({
name,
label,
type = 'text',
placeholder = undefined,
autoCapitalize = 'none',
autoComplete = undefined,
autoCorrect = false,
autoFocus = false,
isRequired = false,
}: {
name: string;
label: string;
type?: 'email' | 'text' | 'number';
placeholder?: string;
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters' | undefined;
autoComplete?: TextInputAndroidProps['autoComplete'] | undefined;
autoCorrect?: boolean;
autoFocus?: boolean;
isRequired?: boolean;
}) {
const {
values,
errors,
touched,
handleChange,
handleBlur,
} = useFormikContext<T>();
return (
<FormControl
isInvalid={name in errors && !!touched[name]}
isRequired={isRequired}
>
<FormControl.Label>
{label}
</FormControl.Label>
<Input
type={type}
onChangeText={handleChange(name)}
onBlur={handleBlur(name)}
value={values[name]}
autoCapitalize={autoCapitalize}
autoComplete={autoComplete}
autoCorrect={autoCorrect}
autoFocus={autoFocus}
placeholder={placeholder ?? label}
accessibilityLabel={label}
/>
<FormControl.ErrorMessage>
{errors[name]}
</FormControl.ErrorMessage>
</FormControl>
);
}THE BROKEN TEST
test('Mandatory fields - display error', async () => {
const {
getByLabelText,
getByText,
container,
} = componentRenderer({
Component: LoginForm,
});
await waitFor(() => container.instance); // wait for the auth provider to remove loading
const emailInput = getByLabelText(/Email/i);
const passwordInput = getByLabelText(/Mot de passe/i);
act(() => {
fireEvent(emailInput, 'onChangeText', 'invlidemail');
fireEvent(passwordInput, 'onChangeText', 'invalidpassword');
// fireEvent.changeText(emailInput, { target: { name: 'email', value: 'invlidemail' } });
// fireEvent.changeText(passwordInput, { target: { name: 'password', value: 'invalidpassword' } });
});
await waitFor(() => container.instance);
expect(emailInput.props.value).toBe('invlidemail');
expect(passwordInput.props.value).toBe('invalidpassword');
expect(getByText(/Veuillez saisir un email valide/i)).toBeDefined();
expect(getByText(/Veuillez saisir un mot de passe valide/i)).toBeDefined();
});versions
"react-native": "0.66.4",
"yup": "^0.32.11"
"formik": "^2.2.9",
"@testing-library/jest-native": "^4.0.4",
"@testing-library/react": "^12.1.2",
"@testing-library/react-native": "^9.0.0",
"react-test-renderer": "17.0.2",
Jest config
module.exports = () => {
const isAndroid = process.env.PLATFORM === 'android';
return {
preset: '@testing-library/react-native',
setupFiles: ['./jestSetupFile.js'],
setupFilesAfterEnv: [
'@testing-library/jest-native/extend-expect',
],
globals: {
'ts-jest': {
tsconfig: './tsconfig.spec.json',
},
},
transform: {
'^.+\\.jsx?$': 'babel-jest',
'^.+\\.tsx?$': 'ts-jest',
},
testEnvironment: 'node',
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
'node',
],
testMatch: [
'**/?(*.)+(spec|test).[jt]s?(x)',
'!**/?(*.web.)+(spec|test).[jt]s?(x)',
isAndroid
? '!**/?(*.ios.)+(spec|test).[jt]s?(x)'
: '!**/?(*.android.)+(spec|test).[jt]s?(x)',
],
transformIgnorePatterns: [
'node_modules/(?!(jest-)?react-native|@react-native)',
],
moduleNameMapper: {
'^.+\\.(png)$': 'identity-obj-proxy',
},
haste: {
defaultPlatform: isAndroid ? 'android' : 'ios',
platforms: [
'android',
'ios',
'native',
],
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/**/@types/graphql.ts',
'!src/**/@types/graphql-errors.ts',
'!src/bundles/**/*.translations.ts',
'!src/reportWebVitals.ts'
],
snapshotResolver: './config/snapshotResolver.native.js',
coverageDirectory: './coverage',
};
};