Как изменить маршруты в модульном тесте для компонента React реагирует на маршрутизатор с помощью крючка useEffect, заключенного в withRouter - PullRequest
0 голосов
/ 16 октября 2019

Я пытаюсь протестировать функциональный компонент с хуком 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();
  });
});

Я пробовал разные способы издеваться над маршрутизатором, но неясно, как изменить маршруты при этих разных попытках.

Проблема сводится к следующим вопросам:

  1. Какиеметод крепления следует использовать? - Я предполагаю монтирование поверх мелкого или рендеринга.
  2. Какой подход следует использовать для обработки оболочки withRouter?
  3. Какой метод следует использовать для переключения параметра маршрута classId в тесте?

Основная цель - просто достичь этого, если условный блок в коде компонента.

...