How to test Next.js pages and components with Jest for Sitecore Headless Development
The primary and important goal of any development process is to build production-ready applications. To achieve this, it is essential to write code that is not only fulfills the functional requirements but also remains stable and reliable over the time.
Testing serves as a safeguard, ensuring that applications function as expected even as new updates are made during the development.
Adopting a Test-driven development (TDD) approach can help maximize your confidence in your code and minimize time spent debugging and resolving bugs that could have slipped to production.
What Is Jest?
Jest is a popular testing framework that is widely used by different JavaScript frameworks. It provides a suite of testing features such as a powerful test runner, automatic mocking, and snapshot testing.
Jest is majorly used to work with react-native based web applications and with react and Next.js. It mostly focuses on simplicity while doing any unit testing for front end application.
Let's install the Jest dev dependencies in your Next js application so that we can test our components using Jest.
Install Dependencies
Install the necessary dependencies as devDependencies by running the following command:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom jest-environment-jsdom babel-jest
Running the above npm command should install the relevant packages and update your package.json file with the following:"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"babel-jest": "^29.4.2",
"jest": "^29.4.2",
"jest-environment-jsdom": "^29.4.2"
}
Configurations
module.exports = {
collectCoverage: true,
// on node 14.x coverage provider v8 offers good speed and more or less good report
coverageProvider: 'v8',
collectCoverageFrom: [
'**/*.{js,jsx,ts,tsx}',
'!**/*.d.ts',
'!**/*.style.ts',
'!**/*.styles.ts',
'!**/*.type.ts',
'!**/*.types.ts',
'!**/*.mock.ts',
'!**/node_modules/**',
'!<rootDir>/out/**',
'!<rootDir>/.next/**',
'!<rootDir>/.next-container/**',
'!<rootDir>/.storybook/**',
'!<rootDir>/.generated/**',
'!<rootDir>/*.config.js',
'!<rootDir>/coverage/**',
'!<rootDir>/scripts/**',
'!<rootDir>/src/stories/**',
'!<rootDir>/src/temp/**',
'!<rootDir>/src/theme/**',
'!<rootDir>/src/lib/**',
'!<rootDir>/src/logger/**',
'!<rootDir>/src/Scripts.tsx',
'!<rootDir>/src/middleware.ts',
'!<rootDir>/src/pages/**',
],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
testEnvironment: 'jsdom',
moduleNameMapper: {
// Handle CSS imports (with CSS modules)
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
// Handle CSS imports (without CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',
// Handle image and font imports
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(jpg|jpeg|png|gif|svg|bmp|webp|avif|eot|otf|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/__mocks__/fileMock.js',
// Handle module aliases
'^@/components/(.*)$': '<rootDir>/components/$1',
},
};
Add this into jest.setup.js:
import '@testing-library/jest-dom';
The final configuration we need to address is adding the npm test command to the package.json file.
"test": "jest",
"test:coverage": "jest --coverage"
import { Typography, Box, Theme } from '@mui/material'; import { Text, RichText, Field, TextField } from '@sitecore-jss/sitecore-jss-nextjs'; import { ComponentProps } from 'lib/component-props'; import { Container } from './ContentBlock.styles'; export type ContentBlockProps = ComponentProps & { fields?: { heading?: TextField; content?: Field<string>; }; }; /** * A simple Content Block component, with a heading and rich text block. * This is the most basic building block of a content site, and the most basic * JSS component that's useful. */ const ContentBlock = (props: ContentBlockProps): JSX.Element => { const { fields } = props; const heading = fields?.heading; const content = fields?.content; const headingValue = heading?.value; const contentValue = content?.value; return ( <> {headingValue || contentValue ? ( <Container> <Box> {headingValue && ( <Typography variant="h4" fontWeight={'bold'} color={(theme: Theme) => theme?.palette?.themeColor?.main} paddingTop={'1rem'} gutterBottom > <Text field={heading} /> </Typography> )} {contentValue && <RichText className="contentblock-content" field={content} />} </Box> </Container> ) : null} </> ); }; export default ContentBlock;
/* eslint-disable @typescript-eslint/ban-ts-comment */ import { render, screen } from '@testing-library/react'; import ContentBlock from './ContentBlock'; describe('ContentBlock component', () => { it('renders heading and content if both are provided in the fields prop', () => { const fields = { heading: { value: 'Test Heading', }, content: { value: 'Test Content', }, }; // @ts-ignore const { getByRole } = render(<ContentBlock fields={fields} />); const headingElement = getByRole('heading', { level: 4 }); expect(headingElement).toBeInTheDocument(); expect(headingElement.tagName).toBe('H4'); expect(headingElement).toHaveTextContent('Test Heading'); expect(screen.getByText('Test Content')).toBeInTheDocument(); }); it('renders only heading if content is not provided in the fields prop', () => { const fields = { heading: { value: 'Test Heading', }, }; // @ts-ignore render(<ContentBlock fields={fields} />); expect(screen.getByText('Test Heading')).toBeInTheDocument(); expect(screen.queryByText('Test Content')).not.toBeInTheDocument(); }); it('renders only content if heading is not provided in the fields prop', () => { const fields = { content: { value: 'Test Content', }, }; // @ts-ignore render(<ContentBlock fields={fields} />); expect(screen.getByText('Test Content')).toBeInTheDocument(); expect(screen.queryByText('Test Heading')).not.toBeInTheDocument(); }); it('renders nothing if fields prop is not provided', () => { // @ts-ignore render(<ContentBlock />); expect(screen.queryByText('Test Heading')).not.toBeInTheDocument(); expect(screen.queryByText('Test Content')).not.toBeInTheDocument(); }); });The above code is self explanatory. Where we have checked three cases as below -
- Only Heading is provided.
- Only Content is provided.
- Nothing is provided.
npm run test
You should see a similar response if the test passes. Furthermore, you can enhance your testing approach by incorporating snapshot tests and end-to-end tests.
npm run test:coverage
Comments
Post a Comment