用玩笑运行时,应用程序未在history.push上重新呈现

时间:2019-08-02 22:17:24

标签: reactjs react-router jestjs react-testing-library

我正在尝试使用jest和react-testing-library测试我的LoginForm组件。成功提交登录表单后,应该使用我的handleLoginSuccess函数在localStorage上设置“ user”项,并使用history.push()将用户导航回主页。这可以在开发环境中的浏览器中使用,但是当我使用Jest渲染组件并模拟出API时,localStorage会更新,但是不会导航到“ /”。

在调用history.push()之前,我尝试过设置localStorage。我不确定在这种情况下是什么原因导致了重新渲染,以及为什么它可以在开发中工作但不能在测试中工作。

Login.test.jsx

import 'babel-polyfill'
import React from 'react'
import {withRouter} from 'react-router'
import {Router} from 'react-router-dom'
import {createMemoryHistory} from 'history'
import {render, fireEvent} from '@testing-library/react'
import Login from '../../pages/Login'
import API from '../../util/api'

jest.mock('../../util/api')


function renderWithRouter(
  ui,
  {route = '/', history = createMemoryHistory({initialEntries: [route]})} = {},
) {
  return {
    ...render(<Router history={history}>{ui}</Router>),
    // adding `history` to the returned utilities to allow us
    // to reference it in our tests (just try to avoid using
    // this to test implementation details).
    history,
  }
}

describe('When a user submits the login button', () => {
  test('it allows the user to login', async () => {
    const fakeUserResponse = {'status': 200, 'data': { 'user': 'Leo' } }

    API.mockImplementation(() => {
      return {
        post: () => {
          return Promise.resolve(fakeUserResponse)
        }
      }
    })

    const route = '/arbitrary-route'
    const {getByLabelText, getByText, findByText} = renderWithRouter(<Login />, {route})

    fireEvent.change(getByLabelText(/email/i), {target: {value: 'email@gmail.com '}})
    fireEvent.change(getByLabelText(/password/i), {target: {value: 'Foobar123'}})
    fireEvent.click(getByText(/Log in/i))

    const logout = await findByText(/Log Out/i)

    expect(JSON.parse(window.localStorage.getItem('vector-user'))).toEqual(fakeUserResponse.data.user)
  })
})

LoginForm.jsx的相关部分

class LoginForm extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      disableActions: false,
      formErrors: null,
    };
  }

  handleLoginSuccess = () => {
    const { loginSuccessCallback, redirectOnLogin, history } = { ...this.props };

    if (loginSuccessCallback) {
      loginSuccessCallback();
    } else {
      history.push('/');
    }
  }

  loginUser = ({ user }) => {
    localStorage.setItem('vector-user', JSON.stringify(user));
  }

  handleLoginResponse = (response) => {
    if (response.status !== 200) {
      this.handleResponseErrors(response.errors);
    } else {
      this.loginUser(response.data);
      this.handleLoginSuccess();
    }
  }

  handleLoginSubmit = (event) => {
    event.preventDefault();

    const {
      disableActions, email, password
    } = { ...this.state };

    if (disableActions === true) {
      return false;
    }

    const validator = new Validator();
    if (!validator.validateForm(event.target)) {
      this.handleResponseErrors(validator.errors);
      return false;
    }

    this.setState(prevState => ({ ...prevState, disableActions: true }));
    new API().post('login', { email, password }).then(this.handleLoginResponse);

    return true;
  }
}

Login.jsx

import React from 'react';
import { withRouter, Link } from 'react-router-dom';
import PropTypes from 'prop-types';

import LoginForm from '../components/LoginForm';

class Login extends React.Component {
  constructor({ location }) {
    super();

    const originalRequest = location.state && location.state.originalRequest;
    this.state = {
      originalRequest
    };
  }

  render() {
    const { originalRequest } = { ...this.state };

    return (
      <div>
        <h1>Login</h1>
        <LoginForm redirectOnLogin={originalRequest && originalRequest.pathname} />
        <Link to="/forgot">Forgot your password?</Link>
      </div>
    );
  }
}
Login.propTypes = {
  location: PropTypes.shape({
    state: PropTypes.shape({
      originalRequest: PropTypes.shape({
        pathname: PropTypes.string
      })
    })
  })
};

export default withRouter(Login);

当前等待的findByText()超时。

1 个答案:

答案 0 :(得分:1)

我认为这是因为在测试中您没有渲染任何Route组件。没有这些响应路由器,就无法知道路由更改时呈现的内容。它将始终呈现Login