Я пытаюсь протестировать функциональный компонент с хуком useEffect, заключенным вRouter. Этот компонент написан на TypeScript, но не позволяйте этому отвлекаться.
import React, { ReactNode, useEffect, useState, Fragment } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { RouterProps } from 'component/hoc/router/Router';
export interface ClassIdChangedProps {
children: ReactNode;
}
const ClassIdChanged = ({
children,
match: {
params: { classId },
},
}: ClassIdChangedProps & RouterProps) => {
const [storedClassId, setStoredClassId] = useState(classId);
useEffect(() => {
if (classId !== storedClassId) {
/** Test should hit this conditional by mocking
* router and grabbing a new classId param
**/
alert('test should hit this');
setStoredClassId(classId);
}
}, [ classId ]);
return (
<Fragment key={classId}>
{ children }
</Fragment>
)
};
export default withRouter<ClassIdChangedProps & RouteComponentProps<any>,
typeof ClassIdChanged>(ClassIdChanged);
Тест должен соответствовать условию, указанному выше, что требует изменения фиктивного маршрутизатора и параметра classId.
import React, { Fragment } from 'react';
import { MemoryRouter } from 'react-router-dom';
import { act } from '@testing-library/react';
import { createMemoryHistory } from 'history';
import { mount } from 'enzyme';
import ClassIdChanged from './ClassIdChanged';
describe('ClassIdChanged', () => {
fit('should rerender children when classId changes on url parameters', () => {
const history = createMemoryHistory();
const spy = jest.fn();
// failed attempt 1 - doesn't hit if block
const wrapper = mount(
<MemoryRouter initialEntries={['/class/1234']} >
<ClassIdChanged>
<Fragment>{spy()}</Fragment>
</ClassIdChanged>
</MemoryRouter>
);
// failed attempt 2 - doesn't hit if block
const wrapper = mount(
<ClassIdChanged>
<Fragment>{spy()}</Fragment>
</ClassIdChanged>, {
wrappingComponent: MemoryRouter,
childContextTypes: { match: { params: { classId: '1234' } } },
});
// failed attempt 3 - doesn't work, doesn't call useEffect,
const wrapper = mount(
<Router>
<ClassIdChanged
match={ { params: { classId: '1234' } } }
>
<div>{ spy() }</div>
</ClassIdChanged>
</Router>
);
// failed attempt 4 - doesn't work, doesn't call useEffect or hit if block
const wrapper = mount(
<MemoryRouter initialEntries={ ['/class/1234'] } >
// @ts-ignore
<ClassIdChanged
match={ { params: { classId: '1234' } } }
>
<div>{ spy() }</div>
</ClassIdChanged>
</MemoryRouter>
);
expect(spy).toHaveBeenCalledTimes(1);
act(() => {
history.push('/class/4321'); // has no effect on any of above route configurations
wrapper.setProps( { match: { params: { classId: '4321' } } } );
expect(spy).toHaveBeenCalledTimes(2);
});
wrapper.unmount();
});
});
Я пробовал разные способы издеваться над маршрутизатором, но неясно, как изменить маршруты при этих разных попытках.
Проблема сводится к следующим вопросам:
- Какиеметод крепления следует использовать? - Я предполагаю монтирование поверх мелкого или рендеринга.
- Какой подход следует использовать для обработки оболочки withRouter?
- Какой метод следует использовать для переключения параметра маршрута classId в тесте?
Основная цель - просто достичь этого, если условный блок в коде компонента.