I am trying to write unit tests for my React application, built with react-query
and ky
. I am constantly faced with the error Error: Uncaught [TypeError: Cannot read properties of undefined (reading 'name')]
when running vitest, but when I run my application on the browser (not in test mode) everything works fine, there are zero errors thrown.
The error is thrown in the following lines (in the JSX markup):
<input
type='text'
value={activeData.page.name} <== this line
onInput={onPageNameChange}
className='w-full h-full px-4 text-2xl'
data-testid='page-name-input'
/>
This is my full code:
export default function NotebookPage() {
const [activeData, setActiveData] = useImmer<ActiveData>({
notebook: { id: -1, name: '', sections: [] },
section: { id: -1, name: '', pages: [] },
page: { id: -1, name: '', content: '' },
});
const initialDataQuery = useInitialData();
useEffect(() => {
if (initialDataQuery.data) {
const data = initialDataQuery.data;
setActiveData({
notebook: data.notebook,
section: data.section,
page: data.page,
});
}
}, [initialDataQuery.data, setActiveData]);
const onContentChange = (content: string) => {
setActiveData((draft) => {
draft.page.content = content;
});
};
const onPageNameChange = (e: FormEvent<HTMLInputElement>) => {
const name = e.currentTarget.value;
setActiveData((draft) => {
draft.page.name = name;
});
};
if (initialDataQuery.isFetching) {
return <p>Loading...</p>;
}
if (initialDataQuery.isError) {
return <p>An error has occurred: {initialDataQuery.error.message}</p>;
}
return (
<div className='h-screen flex'>
<div className='w-1/5'>
<NavPane activeData={activeData} setActiveData={setActiveData} />
</div>
<div className='flex-1 flex flex-col'>
<div className='h-16 border-b border-slate-300'>
<input
type='text'
value={activeData.page.name}
onInput={onPageNameChange}
className='w-full h-full px-4 text-2xl'
data-testid='page-name-input'
/>
</div>
<div className='h-[calc(100%-4rem)] flex'>
<div id='editor-container' className='flex-1 border-r border-slate-300'>
<EditorPane content={activeData.page.content} onContentChange={onContentChange} />
</div>
<div id='preview-container' className='flex-1'>
<PreviewPane rawText={activeData.page.content} />
</div>
</div>
</div>
</div>
);
}
It does not look like there is any way for activeData.page
to be undefined; in fact activeData.section
is also undefined when I inspected the state further (only activeData.notebook
is normal).
This is the test I am writing:
test.only('can parse to markdown', async () => {
const user = userEvent.setup();
render(<NotebookPage />);
await user.clear(await screen.findByTestId('editor'));
await user.type(await screen.findByTestId('editor'), '# hello world');
const h1 = (await screen.findByTestId('preview')).querySelector('h1');
expect(h1).toBeTruthy();
expect(h1).toHaveTextContent('hello world');
});
I have tried adding the following waitFor
before doing my actions thinking maybe the DOM needs more time to update, but the same error happens.
await waitFor(async () => {
await expect(await screen.findByTestId('editor')).toBeInTheDocument();
});
I am going crazy over writing tests the past week 🙁 so any help would be greatly appreciated