I have a react hooks component that fetches data from API server at launch. (in useEffect)
I am trying to create a unit test using Enzyme but Enzyme seems not to wait for the data to be available.
{
"react": "^16.8.6",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
}
My component that is being tested
import React, {useEffect, useState} from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Grid from "@material-ui/core/Grid";
import {getTermsAndConditions} from "../../../../api/auth/authApiConsumer";
import {makeStyles} from "@material-ui/core";
import Typography from '@material-ui/core/Typography';
import Link from "@material-ui/core/Link";
import Modal from '@material-ui/core/Modal';
import Aux from '../../../../hoc/Aux/Aux';
import Box from "@material-ui/core/Box";
const useStyles = makeStyles(theme => ({
txtLabel: {
fontSize: '0.85rem',
},
paper: {
position: 'absolute',
width: 800,
height: 800,
backgroundColor: theme.palette.background.paper,
border: '0px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 4),
outline: 'none',
},
modalContent: {
height: 700,
overflowY: 'scroll',
},
}));
export const TermsAndConditionsCheckbox = props => {
const {onStateChange} = props;
const [state, setState] = useState({
errors: [],
onChange: false,
pristine: false,
touched: false,
inProgress: false,
value: {
termsAndConditions: []
}
});
const [article, setArticle] = useState([]);
useEffect(() => {
async function fetchData() {
const response = await getTermsAndConditions("ENGLISH", "USER").catch(e => {
});
// returns Enabled TAC
const enabledTAC = getEnabledTAC(response);
const latestTACRecord = [];
// When multiple Articles with different version exist, we show the latest one only.
for (const key in enabledTAC) {
latestTACRecord.push(
{
id: enabledTAC[key].articles[0].id,
name: enabledTAC[key].name,
lang: enabledTAC[key].lang,
articleScope: enabledTAC[key].articleScope,
content: enabledTAC[key].articles[0].content,
datePublished: enabledTAC[key].articles[0].datePublished,
state: enabledTAC[key].articles[0].state,
mandatory: enabledTAC[key].mandatory,
}
)
}
setArticle(latestTACRecord);
}
fetchData()
}, []);
const createTACForm = (article, isMandatory) => {
if (isMandatory) {
return (
<FormControlLabel
key={'madatoryAgreement'}
id={'madatoryAgreement'}
name={'madatoryAgreement'}
control={<Checkbox value="mandatoryAgreement"
color="primary"
}/>}
label={<Typography
className={classes.txtLabel}>{createTACLine(article,
isMandatory)}</Typography>}
/>
);
} else if ((article.filter(art => art.mandatory === false)).length > 0) {
return (
<Aux>
{article.filter(art => art.mandatory === false).map(
(value, index) => {
return (
<FormControlLabel
key={'optionalAgreement_' + index}
id={'optionalAgreement_' + index}
name={'optionalAgreement'}
control={<Checkbox value="optionalAgreement"
color="primary"
}/>}
label={<Typography
className={classes.txtLabel}>{createTACLine(
[value],
isMandatory)}</Typography>}
/>
)
})}
</Aux>
)
} else {
return null;
}
};
const createTACLine = (article, isMandatory) => {
return (
<Aux>
{isMandatory ? "[Mandatory] " : "[Optional] "}
{article.filter(art => art.mandatory === isMandatory)
.map(art => {
return (
<Link component="button" variant="body2" id={'art_' + art.id}
key={art.id} onClick={(e) => {
e.preventDefault();
setModalArticleName(art.name);
setModalArticleContent(art.content);
setOpen(true);
}}
name={'art_' + art.id}>
{art.name}
</Link>
);
})
.reduce((prev, curr) => [prev, ", ", curr])
}
</Aux>
);
};
const handleChecked = (isAgreed, ArticleArray) => {
let isRequirementMet = true;
const tacResult = [];
for (const key in ArticleArray) {
if (ArticleArray[key].mandatory && isAgreed === false) {
// User did not agree on mandatory TACs
isRequirementMet = false;
}
tacResult.push({articleId: ArticleArray[key].id, agreed: isAgreed});
}
const updatedState = {
...state,
pristine: isRequirementMet,
value: {
termsAndConditions: tacResult,
}
};
setState(updatedState);
onStateChange(updatedState);
};
const classes = useStyles();
return (
<Grid container spacing={1}>
<Grid item xs={12}>
{article[0] && createTACForm(article, true)}
<Box m={1}/>
{article[0] && createTACForm(article, false)}
</Grid>
</Grid>
)
};
export default TermsAndConditionsCheckbox;
My Unit test code
import {configure} from "enzyme/build";
import Adapter from "enzyme-adapter-react-16/build";
import {createShallow} from "@material-ui/core/test-utils";
import TermsAndConditionsCheckbox from "./TermsAndConditionsCheckbox";
import FormControlLabel from '@material-ui/core/FormControlLabel';
import React from "react";
jest.mock("../../../../api/auth/authApiConsumer");
configure({adapter: new Adapter()});
describe('<TermsAndConditionsCheckbox />', () => {
const handleStateChange = jest.fn();
let shallow;
beforeAll(() => {
shallow = createShallow();
});
let wrapper;
beforeEach(() => {
wrapper = shallow(<TermsAndConditionsCheckbox onStateChange={handleStateChange}/>);
});
it('should render one <FormControlLabel /> element.', () => {
expect(wrapper.find(FormControlLabel)).toHaveLength(1);
});
it('should render names from API', (done) => {
wrapper.update();
console.log(wrapper.find(FormControlLabel).debug());
expect(wrapper.find(FormControlLabel)).toHaveLength(1);
done();
});
});
authApiConsumer (API mock)
export const getTermsAndConditions = (language, articleScope) => new Promise(
function (resolve, reject) {
const articlesToReturn = [
{
"id": 16,
"name": "test1",
"lang": "ENGLISH",
"articleScope": "USER",
"articles": [
{
"id": 30,
"content": "test ut article",
"datePublished": "2019-03-17",
"state": "PUBLISHED"
},
{
"id": 29,
"content": "test ut article2",
"datePublished": "2018-02-16",
"state": "PUBLISHED"
}
],
"mandatory": false,
"enabled": true
}
];
if(language === "ENGLISH"){
resolve(articlesToReturn);
}else{
reject();
}
});
I expect the Enzyme to wait for useEffect and test should pass.