I'm recently learning how to write component tests with jest and the react testing library.
I have an animation in useLayoutEffect, the code works fine on the browser but has error in jest, the code and error message are as follows
Progress/index.tsx
import React, { useRef, useState, useLayoutEffect, useEffect } from "react"
import { useCarousel, useCarouselDispatch } from "../../store/AppContext"
import { ECarouselActionType } from '../../store/types'
import "./styles.css"
export default function Progress( { pid }: { pid: number}) {
const { progressId } = useCarousel()
const dispatch = useCarouselDispatch()
const inner = useRef<HTMLDivElement>(null)
useLayoutEffect(()=>{
if (pid === progressId) {
const keyframes = new KeyframeEffect(
inner.current,
[
{ transform: 'scaleX(0%)', transformOrigin: 'left center' },
{ transform: 'scaleX(100%)', transformOrigin: 'left center' }
],
{ duration: 3000, fill: 'forwards' }
);
const anim = new Animation(keyframes, document.timeline);
anim.play()
const finish = anim.finished
finish.then(() => {
dispatch({
type: ECarouselActionType.SET_MOVE
})
})
}
}, [progressId])
return (
<div className="Progress">
<div className="progress-wrap">
<div className="progress-bar progress-outer">
<div
className="progress-bar progress-inner"
ref={inner}
style={{ background: pid === progressId ? '#fff' : '#9aa0a6'}}
>
</div>
</div>
</div>
</div>
)
}
App.test.tsx // Not the actual test case, just to see the test running process
/**
* @jest-environment jsdom
*/
import React from 'react';
import '@testing-library/jest-dom';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/Tablet/i);
expect(linkElement).toBeInTheDocument();
});
setupTests.ts
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';
package.json // create-react-app eject
{
"name": "react-assignment",
"version": "0.1.0",
"private": true,
"dependencies": {
"jest-environment-jsdom": "^27.0.6",
"@babel/core": "^7.16.0",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
"@svgr/webpack": "^5.5.0",
"babel-jest": "^27.4.2",
"babel-loader": "^8.2.3",
"babel-plugin-named-asset-import": "^0.3.8",
"babel-preset-react-app": "^10.0.1",
"bfj": "^7.0.2",
"browserslist": "^4.18.1",
"camelcase": "^6.2.1",
"case-sensitive-paths-webpack-plugin": "^2.4.0",
"css-loader": "^6.5.1",
"css-minimizer-webpack-plugin": "^3.2.0",
"dotenv": "^10.0.0",
"dotenv-expand": "^5.1.0",
"eslint": "^8.3.0",
"eslint-config-react-app": "^7.0.0",
"eslint-webpack-plugin": "^3.1.1",
"file-loader": "^6.2.0",
"fs-extra": "^10.0.0",
"html-webpack-plugin": "^5.5.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^27.4.3",
"jest-resolve": "^27.4.2",
"jest-watch-typeahead": "^1.0.0",
"mini-css-extract-plugin": "^2.4.5",
"postcss": "^8.4.4",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-loader": "^6.2.1",
"postcss-normalize": "^10.0.1",
"postcss-preset-env": "^7.0.1",
"prompts": "^2.4.2",
"react": "^17.0.2",
"react-app-polyfill": "^3.0.0",
"react-dev-utils": "^12.0.0",
"react-dom": "^17.0.2",
"react-refresh": "^0.11.0",
"resolve": "^1.20.0",
"resolve-url-loader": "^4.0.0",
"sass-loader": "^12.3.0",
"semver": "^7.3.5",
"source-map-loader": "^3.0.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.0.2",
"terser-webpack-plugin": "^5.2.5",
"webpack": "^5.64.4",
"webpack-dev-server": "^4.6.0",
"webpack-manifest-plugin": "^4.0.2",
"workbox-webpack-plugin": "^6.4.1"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.4.0",
"@types/node": "^17.0.13",
"@types/react": "^17.0.38",
"@types/react-dom": "^17.0.11",
"typescript": "^4.5.5"
},
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"test": "node scripts/test.js"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"roots": [
"<rootDir>/src"
],
"collectCoverageFrom": [
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts"
],
"setupFiles": [
"react-app-polyfill/jsdom"
],
"setupFilesAfterEnv": [
"<rootDir>/src/setupTests.ts"
],
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
],
"testEnvironment": "jsdom",
"transform": {
"^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
},
"transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
],
"modulePaths": [],
"moduleNameMapper": {
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"web.js",
"js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json",
"web.jsx",
"jsx",
"node"
],
"watchPlugins": [
"jest-watch-typeahead/filename",
"jest-watch-typeahead/testname"
],
"resetMocks": true
},
"babel": {
"presets": [
"react-app"
]
}
}
Running yarn test shows the following error
FAIL src/App.test.tsx
✕ renders learn react link (46 ms)
● renders learn react link
ReferenceError: KeyframeEffect is not defined
10 | useLayoutEffect(()=>{
11 | if (pid === progressId) {
> 12 | const keyframes = new KeyframeEffect(
| ^
13 | inner.current,
14 | [
15 | { transform: 'scaleX(0%)', transformOrigin: 'left center' },
at src/components/Progress/index.tsx:12:25
Any idea how to fix this?
You got this error because jest is not using an actual browser when running test, but a NodeJS environment with
jest-dom, which is a mock of many browser feature but unfortunately does not provide the classKeyframeEffect.You can either mock the
KeyframeEffectby yourselfOr prevent this code to be executed in an environment where
KeyframeEffectdoes not exist, example: old browser or testing