I tried countless different ways to interact with the date input but nothing worked. Sometimes the default date just stayed the same, and sometimes it turned into the empty string. I was never able to change the value to a new date. I tried among other things:
const startDate = screen.getByLabelText('from');
act(() => {
fireEvent.change(startDate, {target: {value: '2019-10-08'}});
});
this turned it into empty string.
act(() => {
userEvent.type(startDate, '08102019'); // and all variations with dashes or slashes and with the year first
});
this had no effect on the value (using userEvent==13.5.2
).
const user = userEvent.setup();
await act(async () => {
await user.type(startDate, '08102019');
});
No effect (using the latest userEvent
, 14+).
And various other combinations using fireEffect
to click or userEvent
to click or userEvent.keyboard
for example.
Here's a minimal version of my code reproducing the error
DateComp.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import DateComp from './DateComp';
import '@testing-library/jest-dom';
test('changing date value works', async () => {
render(<DateInput onChange={()=>{return;}} className="" />);
const startDate = screen.getByLabelText('from');
const endDate = screen.getByLabelText('to');
act(() => {
fireEvent.change(startDate, {target: {value: '2019-10-08'}});
});
act(() => {
fireEvent.change(endDate, {target: {value: '2020-10-08'}});
});
expect(startDate.getAttribute('value')).toEqual('2019-10-08');
expect(endDate.getAttribute('value')).toEqual('2020-10-08');
});
DateComp.tsx
import { useState, useEffect } from 'react';
interface IProps {
onChange: CallableFunction,
className: string,
}
const DateComp = ({ onChange, className }: IProps): JSX.Element => {
// Default dates
const dateFrom = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString().substring(0, 10); // 7 days ago
const dateTo = new Date().toISOString().substring(0, 10); // today
const [selectedFrom, setSelectedFrom] = useState(dateFrom);
const [selectedTo, setSelectedTo] = useState(dateTo);
const handleFrom = (e: React.ChangeEvent<HTMLInputElement>) => {
setSelectedFrom(e.target.value);
};
const handleTo = (e: React.ChangeEvent<HTMLInputElement>) => {
setSelectedTo(e.target.value);
};
return (
<div className={className}>
<label htmlFor='date_inputs'>Period</label>
<div id='date_inputs' className='date-inputs'>
<label htmlFor='date_from' className='screen-reader-only'>from</label>
<input id='date_from' type='date' defaultValue={dateFrom} onChange={handleFrom} />
<label htmlFor='date_to'>to</label>
<input id='date_to' type='date' defaultValue={dateTo} onChange={handleTo} />
</div>
</div>
);
};
export default DateComp;
package.json
{
...,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "./node_modules/.bin/react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...,
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.24",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0",
"msw": "^1.2.3",
"react-scripts": "5.0.1"
},
"jest": {
"transformIgnorePatterns": [
"node_modules/(?!@ngrx|(?!deck.gl)|ng-dynamic)"
]
}
}
Turns out my problem wasn't with the test, but with my React code. I had an uncontrolled <input type="date">
element because I was using defaultValue
instead of value
.
Changing to value
and using the state instead of the default dates (thereby making the element controlled I believe) fixed this. Working code:
DateComp.tsx
<label htmlFor='date_from' className='screen-reader-only'>from</label>
<input id='date_from' type='date' value={selectedFrom} onChange={handleFrom} />
<label htmlFor='date_to'>to</label>
<input id='date_to' type='date' value={selectedTo} onChange={handleTo} />
DateComp.test.tsx
const startDate = screen.getByLabelText('from');
act(() => {
fireEvent.change(startDate, {target: {value: '2015-12-10'}});
});
expect(startDate.getAttribute('value')).toEqual('2019-10-08');
P.S. using any combination of user/userEvent
still doesn't work. If anyone figures that out let me know. Maybe the input type="date"
element is still too new to be able to interact with nicely through the tests?
P.P.S it's also interesting to me the date component worked totally fine from the users perspective using defaultValue
, only the test wasn't able to modify the value.