Как выполнить модульное тестирование пользовательского интерфейса Texfield в Formik? - PullRequest
0 голосов
/ 13 февраля 2020

Я пытаюсь выполнить модульный тест в форме входа, имитируя события для ввода неправильных значений в текстовые поля, чтобы в тесте были ошибки. У меня есть следующие настройки:

SignInPage. js

import {CssBaseline, withStyles} from '@material-ui/core';
import Avatar from '@material-ui/core/Avatar'
import Button from '@material-ui/core/Button';
import Checkbox from '@material-ui/core/Checkbox';
import Container from '@material-ui/core/Container';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Grid from '@material-ui/core/Grid';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import LockOutlinedIcon from '@material-ui/icons/LockOutlined';
import {Formik} from 'formik';
import PropTypes from 'prop-types';
import React, {Component} from 'react';
import {connect} from 'react-redux';
import * as yup from 'yup';
import {actions} from '../../../_actions/user.actions';
import Footer from '../../../_components/footer/footer';
import Navbar from '../../../_components/navbar/navbar';
import {history} from '../../../_helpers/history';
import config from '../../../config';

const useStyles = theme => ({
  paper: {
    marginTop: theme.spacing(8),
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
  },
  main: {
    marginTop: theme.spacing(8),
    marginBottom: theme.spacing(2),
    alignItems: 'center'
  },
  footer: {
    padding: theme.spacing(3, 2),
    marginTop: 'auto'
  },
  avatar: {
    margin: theme.spacing(1),
    backgroundColor: theme.palette.secondary.main,
  },
  form: {
    width: '100%', // Fix IE 11 issue.
    marginTop: theme.spacing(1),
  },
  submit: {
    margin: theme.spacing(3, 0, 2),
  }
});

export class SignInPage extends Component {
  constructor(props) {
    super(props);

    // reset login status
    this.props.signOut();

    this.state = {
      values: {
        email: '',
        password: '',
        remember: undefined
      }
    };

    this.validationSchema = yup.object().shape({
      email: yup.string().email().required('Please enter email address'),
      password: yup.string().required('Please enter password'),
    });
    this.validateAndSubmit = this.validateAndSubmit.bind(this);
  }

  validateAndSubmit(values, {setSubmitting}) {
    this.validationSchema.isValid(values)
      .then(valid => {
        if (valid) {
          this.props.signIn(values);
        }
      })
  }

  navigateToForgotPass(e) {
    history.push('/forgot-password');
  };

  componentDidUpdate() {
    this.isLoggedIn = this.props.isLoggedIn && this.props.tokens !== null;
    if (this.isLoggedIn) history.push('/home');
  }

  render() {
    const {classes, isFetching} = this.props;
    return (
      <React.Fragment>
        <CssBaseline/>
        <Navbar appName={config.appName} data-test="navbarComponent"/>
        <Container component="main" maxWidth="xs" data-test="signInContainer">
          <div className={classes.paper}>
            <Avatar className={classes.avatar}>
              <LockOutlinedIcon/>
            </Avatar>
            <Typography component="h1" variant="h5">
              Sign in
            </Typography>
            <Formik initialValues={{email: '', password: ''}}
                    validationSchema={this.validationSchema}
                    onSubmit={this.validateAndSubmit} data-test="formik">
              {({
                  touched,
                  errors,
                  handleChange,
                  handleBlur,
                  handleSubmit,
                }) => (
                <form className={classes.form} onSubmit={handleSubmit} noValidate data-test="signinForm">
                  <TextField
                    variant="outlined"
                    margin="normal"
                    required
                    fullWidth
                    id="email"
                    label="Email Address"
                    name="email"
                    autoComplete="email"
                    onChange={handleChange}
                    onBlur={handleBlur}
                    disabled={isFetching}
                    error={errors.email && touched.email}
                    helperText={(errors.email && touched.email) && errors.email}
                    data-test="emailInput"
                  />
                  <TextField
                    variant="outlined"
                    margin="normal"
                    required
                    fullWidth
                    name="password"
                    label="Password"
                    type="password"
                    id="password"
                    autoComplete="current-password"
                    onChange={handleChange}
                    onBlur={handleBlur}
                    disabled={isFetching}
                    error={errors.password && touched.password}
                    helperText={(errors.password && touched.password) && errors.password}
                    data-test="passwordInput"
                  />
                  <FormControlLabel
                    control={<Checkbox color="primary" id="remember" name="remember" disabled={isFetching} onChange={handleChange}/>}
                    label="Remember me"
                    data-test="rememberMe"
                  />
                  <Button
                    type="submit"
                    fullWidth
                    variant="contained"
                    color="primary"
                    className={classes.submit}
                    disabled={isFetching}
                    data-test="submitButton"
                  >
                    Sign In
                  </Button>
                  <Grid container>
                    <Grid item xs>
                      <Button color="primary" size="small" onClick={this.navigateToForgotPass} data-test="forgotPassword">Forgot password?</Button>
                    </Grid>
                  </Grid>
                </form>
              )}
            </Formik>
          </div>
        </Container>

        {/* Footer */}
        <footer className={classes.footer}>
          <Footer appName={config.appName} data-test="footerComponent"/>
        </footer>
        {/* End footer */}
      </React.Fragment>
    );
  }
}

SignInPage.propTypes = {
  classes: PropTypes.object.isRequired
};

function mapState(state) {
  const {isLoggedIn, tokens, isFetching} = state.user;
  return {isLoggedIn, tokens, isFetching};
}

const actionCreators = {
  signOut: actions.signOut,
  signIn: actions.signIn
};

const connectedSignInPage = connect(mapState, actionCreators)(SignInPage);

export default withStyles(useStyles)(connectedSignInPage);

SignInPage.test. js

import TextField from '@material-ui/core/TextField';
import {mount, shallow} from 'enzyme';
import React from 'react';
import {Provider} from 'react-redux'
import {userConstants} from '../../../_constants';
import {checkProps, findByDataAttr, findByDataAttrWhenMounted, testStore} from '../../../_utils';
import SignInPage from './SignInPage';


const props = {
  classes: {paper: 'someprop'},
};

const setUp = (props = {}) => {
  return shallow(<SignInPage {...props}/>);
};

const setUpWithStore = (props = {}, initialStates = {}) => {
  const store = testStore(initialStates);
  return mount(
    <Provider store={store}>
      <SignInPage {...props}/>
    </Provider>
  );
};

describe('Checking Components', () => {
    let component;
    beforeEach(() => {
      const initialStates = {};
      component = setUpWithStore(props, initialStates);
    });

    it.only('should show error when nothing entered', () => {
      const emailInput = component.find(TextField).at(0);
      emailInput.simulate('blur', {target: {value: '20'}});
      console.log(emailInput.props());
      expect(emailInput.props().error).toBe(true);
    });
  });
});

Utils. js

import checkPropTypes from 'check-prop-types';
import {applyMiddleware, createStore} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './../_reducers';


const findByDataAttr = (component, attr) => {
  return component.find(`[data-test='${attr}']`);
};

const findByDataAttrWhenMounted = (component, attr) => {
  return component.find(`[data-test='${attr}']`).hostNodes();
};

const checkProps = (component, expectedProps) => {
  return checkPropTypes(component.propTypes, expectedProps, 'props', component.name);
};

const testStore = (initialState) => {
  const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
  return createStoreWithMiddleware(rootReducer, initialState);
};

module.exports = {findByDataAttr, checkProps, testStore, findByDataAttrWhenMounted};

Результат теста:

{
      variant: 'outlined',
      margin: 'normal',
      required: true,
      fullWidth: true,
      id: 'email',
      label: 'Email Address',
      name: 'email',
      autoComplete: 'email',
      onChange: [Function],
      onBlur: [Function],
      disabled: undefined,
      error: undefined,
      helperText: undefined,
      'data-test': 'emailInput'
    }

Error: expect(received).toBe(expected) // Object.is equality

Expected: true
Received: undefined

Кажется, что error не определено. Есть ли обходной путь для этой ситуации?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...