vendredi 30 décembre 2016

React TDD - Setting the component-level state asynchronously mocha + chai

I'm trying to test my React application and I can successfully test reducers and component states with Simulate on a onClick handler, but I can't seem to test setting a component state when the change in state is based on an async request. Would love some help :) Thanks!

My test.utils file

import jsdom from 'jsdom';
import _$ from 'jquery'; // _$ instead of $ because '$', will try and reach out to browser which is not avail.
import TestUtils from 'react-addons-test-utils';
import ReactDOM from 'react-dom';
import chai, { expect } from 'chai';
import React from 'react';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducers from '../../src/reducers/index.js';
import chaiJquery from 'chai-jquery';

// 1. Set up testing environment to run like a browser in the command line
// window (browser global env)--> global (in terminal)
  global.document = jsdom.jsdom("<!doctype html><html><body></body></html>");
  global.window = global.document.defaultView;
    // hooking jQuery to our version of window
    // don't go out to the DOM, just be responsible for the window created above
    const $ = _$(global.window);

// 2. Build 'renderComponent' helper that should render a given react class
  /**
   * [renderComponent description]
   * @param  Class    ComponentClass   React Class or Functional Component to render
   * @param  Object   props            Props for the Component to render
   * @param  Object   state            Setting a temporary state for test purposes
   * @return jQueryWrapped DOM selection
   */
  function renderComponent(ComponentClass, state, props) {
    // componentInstance --> rendered instance of our component
    // <Provider> element is required because redux expects components to be
    // rendered within a <Provider> element
    const componentInstance = TestUtils.renderIntoDocument(
      <Provider store={createStore(reducers, state)}>
        <ComponentClass {...props}/>
      </Provider>
    );

    // ReactDOM.findDOMNode --> gets reference to our componentInstance HTML
    return $(ReactDOM.findDOMNode(componentInstance)); // produces HTML
  }

// 3. Build helper for simulating events
  // Adding simulate to jQuery library
  $.fn.simulate = function(eventName, value) {
    // "this" refers to the jQuery element form $('element') e.g. $('div');
    // jQuery selectors return an array
    if (value) {
      this.val(value);
    }
    TestUtils.Simulate[eventName](this[0]);
  }

// 4. Setup chai-jQuery
  chaiJquery(chai, chai.util, $);

export { renderComponent, expect };

My Verify test

// Test Helpers
import {renderComponent, expect} from '../../../helpers/test_helper.js';
import TestUtils from 'react-addons-test-utils';

// Component to Test
import Verify from '../../../../src/modules/App/components/Authentication/Verify';
import authReducer from '../../../../src/reducers';

import {
  USER_VERIFIED,
  USER_NOT_VERIFIED,
  USER_VERIFIED_UNDEFINED 
} from '../../../../src/actions/types.js';

describe('Components - Authentication/Verify', () => {
  let component;

  it('Shows verified notice when verified', () => {
    const action = { type: USER_VERIFIED };
    component = renderComponent(Verify, { authReducer: { userVerified: true} });
    console.log('component: ', component)
    // if (authReducer({}, action).authReducer.userVerified) {
    //   console.log('verified: ', component)
      expect(component.find('.verified-content').length).to.eql(1);
    // }
  });
});

My Verify component

// Other Libraries
import React, { Component } from 'react';
import { connect } from 'react-redux';

// Our Code
import { verify } from '../../../../actions/auth.js';
import { BUSINESS_TEAM_NAME } from '../../../../../utils/constants.js';


class Verify extends Component {
  constructor(props) {
    super(props)
    this.state = { validURL: false }
  }

  componentWillMount() {
    let params = window.location.href,
        token = null;

    // Check if URL contains token required by server
    if (params.includes('?') && params.includes('=') && params[1].split('=')) {
      if (params.indexOf('token')) {
        let index;
        params = params[1].split('=');
        index = params.indexOf('token');
        token = params[index + 1];
        this.setState({ validURL: true });
      } else { this.setState({ validURL: false }) }
    }

    // If valid token exists, verify the user
    token != null ? this.props.verify(token) : '';
  }

  // Once user is successfully verified, render this content.
  renderVerifiedContent() {
    return (
        <div className="container container-center-content verified-content">
          <h2>Welcome to {BUSINESS_TEAM_NAME}</h2>
          <p>Your account has now been verified.</p>
        </div>
      )
  }

  // Render verification error
  renderLoading() {
    return (
        <div>Attempting to verify your account...</div>
      )
  }

  renderUnableToVerify() {
    return (
        <div className="container container-center-content">
          <h2>Verification Error:</h2>
          <p>We are unable to verify your account right now. Please check that you clicked on the link in your email. If that doesn't work, try right-click and copy the link. Otherwise, try again later. We apologise for this inconvenience.</p>
          <p>Hope you have a great day!</p>
          <p>{BUSINESS_TEAM_NAME} Team</p>
        </div>
      )
  }

  render() {
    return this.state.validURL 
      ? this.props.userVerified != undefined && this.props.userVerified 
        ? this.renderVerifiedContent() 
        // Only render loading if request is still in progress,
        // otherwise user might still think its being verified
        : this.props.userVerified == undefined ? this.renderLoading() : this.renderUnableToVerify()
      : this.renderUnableToVerify()
  }
}

function mapStateToProps(state) {
  return {
    userVerified: state.authReducer.userVerified
  }
}

export default connect(mapStateToProps, { verify })(Verify);

Aucun commentaire:

Enregistrer un commentaire