Reputation: 45
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]);
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). useInitialData
returns a useQuery
hook, with the queryFn
being a simple ky.get(<url>)
that returns { notebook, section, page }
. I am using MSW to mock this endpoint, so it is not possible for it to return undefined
.
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 () => {
expect(await screen.findByTestId('editor')).toBeInTheDocument();
});
If I keep re-running the test, it passes like ~30% of the time, which is puzzling. I have also checked all calls to setActiveData
, and unless I am missing something, none of the calls were made with any undefined
data.
Upvotes: 0
Views: 628
Reputation: 45
I figured out the problem... Turns out it was the -1
ids when I defined my activeData
state. In my child components I use activeData.notebook.id
and activeData.section.id
to fetch more data, but after useInitialData
, the set state call hasn't fully resolved so -1
was used in my api calls. Since -1
isn't a proper id my api resolves to an empty array, which I did not check for and immediately tried to do array[0]
, hence causing undefined
in my activeData
state.
My next question now is how come in the development server when developing on the browser, this error isn't caught...
Upvotes: 0