Reputation: 329
I recently updated Jest to it's latest 28. version and my unit tests stopped working, particularly on tests that validated my form with Yup and Formik and showed the error messages. If I rollback the Jest version it does indeed work fine.
Here is my package.json dependencies
"dependencies": {
"@chakra-ui/icons": "^1.0.16",
"@chakra-ui/react": "^1.6.10",
"@emotion/react": "^11.5.0",
"@emotion/styled": "^11.3.0",
"@types/styled-components": "^5.1.15",
"axios": "^0.24.0",
"formik": "^2.2.9",
"framer-motion": "^4.1.17",
"graphql": "^16.3.0",
"graphql-request": "^4.0.0",
"next": "^12.2.0",
"next-i18next": "^10.2.0",
"nookies": "^2.5.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-icons": "^4.3.1",
"react-number-format": "^4.9.3",
"react-query": "^3.34.6",
"styled-components": "^5.3.3",
"yup": "^0.32.11"
},
"devDependencies": {
"@testing-library/dom": "^8.16.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^13.5.0",
"@types/node": "18.0.5",
"@types/react": "17.0.33",
"eslint": "7.32.0",
"eslint-config-next": "12.0.1",
"eslint-config-prettier": "^8.3.0",
"jest": "^28.1.3",
"jest-environment-jsdom": "^28.1.3",
"msw": "^0.39.2",
"react-test-renderer": "^17.0.2",
"typescript": "4.4.4"
}
Here is my jest.config.js
// jest.config.js
const nextJest = require('next/jest');
const createJestConfig = nextJest({
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
dir: './',
});
// Add any custom config to be passed to Jest
const customJestConfig = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
moduleNameMapper: {
// Handle module aliases (this will be automatically configured for you soon)
'^@/components/(.*)$': '<rootDir>/components/$1',
'^@/pages/(.*)$': '<rootDir>/pages/$1',
},
moduleDirectories: ['node_modules', '<rootDir>/'],
testEnvironment: 'jsdom',
};
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig);
Here is my failling test
it('validates empty form values', async () => {
server.use(
graphql.query('GetAllProducts', (req, res, ctx) => {
return res(ctx.status(200), ctx.data(getProductsResponseBody));
})
);
const signupResult = renderWithDefaultClient(<ProductPage />);
await userEvent.click(
await signupResult.findByText('addProductButtonLabel')
);
await userEvent.click(await signupResult.findByText('createButtonLabel'));
expect(await signupResult.findByText('required')).toBeInTheDocument();
});
And here is the component that I'm testing
import {
Button,
FormControl,
FormErrorMessage,
FormLabel,
HStack,
Input
} from '@chakra-ui/react';
import { Field, Form, Formik } from 'formik';
import { useTranslation } from 'next-i18next';
import React from 'react';
import * as Yup from 'yup';
import {
FormProduct,
IProduct
} from '../../interfaces/pages/product/ProductInterface';
import { returnEmptyStringIfNull } from '../../utils/StringUtils';
const ProductForm = () => {
const { t } = useTranslation(['product', 'shared']);
const ProductFormSchema = Yup.object().shape({
code: Yup.string().max(45, t('characterLimitFortyFive', { ns: 'shared' })),
name: Yup.string()
.max(45, t('characterLimitFortyFive', { ns: 'shared' }))
.required(t('required', { ns: 'shared' })),
});
const initialValues: FormProduct = {
code: returnEmptyStringIfNull(product.code),
name: returnEmptyStringIfNull(product.name),
};
const onSubmitForm = (productForm: FormProduct) => {
createMutation.mutate(productForm);
};
return (
<>
<Formik
initialValues={initialValues}
validationSchema={ProductFormSchema}
onSubmit={(form) => {
onSubmitForm(form);
}}
>
{(props) => (
<Form data-testid='productForm' style={{ marginTop: '20px' }}>
<HStack spacing={'24px'} w={'2xl'} mb={3}>
<Field name='code'>
{
// @ts-ignore
({ field, form }) => (
<FormControl
isInvalid={form.errors.code && form.touched.code}
>
<FormLabel htmlFor='code'>{t('codeLabel')}</FormLabel>
<Input {...field} id='code' data-testid='code' />
<FormErrorMessage>{form.errors.code}</FormErrorMessage>
</FormControl>
)
}
</Field>
<Field name='name'>
{
// @ts-ignore
({ field, form }) => {
return (
<FormControl
isInvalid={form.errors.name && form.touched.name}
isRequired
>
<FormLabel htmlFor='name'>{t('nameLabel')}</FormLabel>
<Input {...field} id='name' data-testid='name' />
<FormErrorMessage>{form.errors.name}</FormErrorMessage>
</FormControl>
);
}
}
</Field>
</HStack>
<HStack spacing={'24px'} mt={4}>
<Button colorScheme='blue' type='submit'>
{t('createButtonLabel', { ns: 'shared' })}
</Button>
</HStack>
</Form>
)}
</Formik>
</>
);
};
export default ProductForm;
As you can see I'm trying to validate that the required error message is indeed getting shown, but after I update jest and install jsdom it doesn't work.
It works again by installing Jest 27.5.1, all of my failling tests are due to errors by formik and yup not being shown.
Upvotes: 0
Views: 2777
Reputation: 120
Doing a bit of searching I found this GitHub issue: https://github.com/jaredpalmer/formik/issues/544
Try adding noValidate
to your form component. I got it to pass when making that change.
Upvotes: 1
Reputation: 329
Apparently JSDOM is now validating HTML5 form inputs when it has the required attribute set, after I took out the "isRequired" on ChakraUI it worked, even though that it's what's expected in a real browser environment but previous versions like the one that I was using was not validating the form as it should have.
A reference to JSDOM can be seen here: https://github.com/jsdom/jsdom/issues/2898
Upvotes: 0