0

I am trying to fill a Text Input and verify that the text is filled correctly, accessing the component and getting its value.

I have succeeded in doing so, but without using redux, ie using the native states of react-native. this.state.

Component Code:

//inside constructor
this.state = {
  email: ''
}

<TextInput value={this.state.email} onChangeText={(text) => {
  console.log('Here change email text!!! ==> ', text);
  this.setState({
    email: text
  })
}} />

Test File Code:

import LoginScreen from '../../App/Containers/LoginScreen' // => connected component.. exported with `export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)`
import configureStore from 'redux-mock-store'
import { shallow } from 'enzyme'

import Actions, { reducer, INITIAL_STATE } from '../../App/Redux/Reducers/UserReducer'


const initialState = {
  user: {
    email: 'mockState email',
    password: '',
    requesting: 0,
    userData: null,
    loginFinish: false,
    errorMessage: null
  }
}

const mockStore = configureStore([]);
let store = mockStore(initialState);

const wrapper = shallow(
  <LoginScreen/>,
  { context: { store: store } },
);

test('>>>>> LoginScreen component renders correctly', () => {
  expect(wrapper.dive()).toMatchSnapshot();
});


test('>>>>> Login button Press', () => {
  let render = wrapper.dive();

  const textInputProps = render.find('TextInput'); //getting text input from render
  console.log(`textInputProps.getNode(1).props.value BEFORE ====>`, textInputProps.getNodes()[0].props.value);

  textInputProps.first().simulate('changeText', 'My new value'); // executing onChangeText inside render of component

  const textInputProps2 = render.find('TextInput'); //getting text input again for check changes
  console.log(`textInputProps2.getNode(1).props.value====>`, textInputProps2.getNodes()[0].props.value); 

  const state = store.getState(); //verifying internal `initialState`.. NOT CHANGES
  console.log('state ===> ', state);

});

I have relied on this link

Running test logs

yarn test v0.24.6
$ jest 
 PASS  Tests/Containers/loginScreenTest.js
  ✓ >>>>> LoginScreen component renders correctly (282ms)
  ✓ >>>>> Login button Press (33ms)

  console.log Tests/Containers/loginScreenTest.js:60
    textInputProps.getNode(1).props.value BEFORE ====> 

  console.log App/Containers/LoginScreen.js:124
    Here change email text!!! ==>  My new value

  console.log Tests/Containers/loginScreenTest.js:67
    textInputProps2.getNode(1).props.value====> My new value => (!!!WORKS!!!)

  console.log Tests/Containers/loginScreenTest.js:86
    state ===>  { user: 
       { email: 'mockState email',
         password: '',
         requesting: 0,
         userData: null,
         loginFinish: false,
         errorMessage: null } }

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        2.337s, estimated 3s
Ran all test suites.
✨  Done in 3.10s.

as you can see in the logs textInputProps2.getNode(1).props.value ====> show me the value as expected.

So far so good

Now passing everything to a reducer, with the redux structure, we will see the text input as follows

<TextInput value={this.props.user.email} style={styles.textInputs} placeholder={'Email'} autoCapitalize={'none'} onChangeText={(text) => {
  console.log('Here change email text!!! ==> ', text);
  this.props.email_typing(text);
}} />

Connected logic

const mapStateToProps = (state) => {
  return {
    user: state.user
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    email_typing: (text) => dispatch(UserReducer.email_typing(text)),
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)

My UserReducer File

import { createReducer, createActions } from 'reduxsauce'
import Immutable from 'seamless-immutable'

/* ------------- Types and Action Creators ------------- */

const { Types, Creators } = createActions({
  email_typing: ['email'],
})

export const LoginTypes = Types
export default Creators

/* ------------- Initial State ------------- */

export const INITIAL_STATE = Immutable({
  email: ''
})

/* ------------- Reducers ------------- */


// state.merge undefined error: https://github.com/infinitered/ignite/pull/20#issuecomment-202550408. Fixed including in Inmutable
export const emailTyping = (state, { email }) => {
  console.log('Email Typing changes !!! in original reducer')
  return Immutable(state).merge({ email })
}


/* ------------- Hookup Reducers To Types ------------- */

export const reducer = createReducer(INITIAL_STATE, {
  [Types.EMAIL_TYPING]: emailTyping,
})

Given this change, the idea is that the initialState within the Test File changes to INITIAL_STATE imported value.

Something like:

const mockStore = configureStore([]);
let store = mockStore(INITIAL_STATE);

but, when i run the test again. Show me the next error:

 ● >>>>> LoginScreen component renders correctly

    TypeError: Cannot read property 'email' of undefined

even if I keep the initialState instead of the INITIAL_STATE, I do not get the above error, but I can not get the text input to take the change.

Running Test Logs

yarn test v0.24.6
$ jest 
 PASS  Tests/Containers/loginScreenTest.js
  ✓ >>>>> LoginScreen component renders correctly (345ms)
  ✓ >>>>> Login button Press (24ms)

  console.log Tests/Containers/loginScreenTest.js:58
    textInputProps.getNode(1).props.value BEFORE ====> mockState email

  console.log App/Containers/LoginScreen.js:120
    Here change email text!!! ==>  My new value

  console.log Tests/Containers/loginScreenTest.js:61
    textInputProps2.getNode(1).props.value====> mockState email => **(!! HERE !!!, THE VALUE IS BEING THE PREVIOUS ONE AND IGNOR THE CHANGE)**

  console.log Tests/Containers/loginScreenTest.js:79
    state ===>  { user: 
       { email: 'mockState email',
         password: '',
         requesting: 0,
         userData: null,
         loginFinish: false,
         errorMessage: null } }

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        2.904s
Ran all test suites.
✨  Done in 3.68s.

Check textInputProps2.getNode(1).props.value====> log to check that this is not useful.

I think that the const initialState declared inside test file It is not being affected by the changes made in the actual reducer when this.props.email_typing(text) action is called;

I have not found the way to connect the actions with the states in the reducer and to be able to load them inside JEST.

I know it's a bit long and I appreciate your time reading it. I tried to leave it the best explained and as much information as possible. Thank you very much and I look forward to any response.

1
  • import LoginScreen from '../../App/Containers/LoginScreen' // => connected component.. exported with `export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen) this sounds like an import issue , did you export the component as well (you should have 2 exports in your tested file, one for the connect and one for the actual component) Commented Oct 6, 2017 at 6:29

1 Answer 1

1

I guess you want to do some integration test here. It is possible to achieve what you are trying like that :

import { createStore, combineReducers } from 'redux';
import { reducer } from '.../UserReducer';

// create a real store with the needed reducer(s)
const store = createStore(combineReducers({ user: reducer }));

const wrapper = shallow(
  <LoginScreen/>,
  { context: { store } },
);

// ...

test('>>>>> Login button Press', () => {
  let render = wrapper.dive();

  const textInputProps = render.find('TextInput');
  console.log(`value BEFORE ====>`, textInputProps.getNodes()[0].props.value);

  textInputProps.first().simulate('changeText', 'My new value');

  // Force the component to update after changing state 
  render = wrapper.update().dive();

  const textInputProps2 = render.find('TextInput');
  console.log(`value AFTER ====>`, textInputProps2.getNodes()[0].props.value); 

  const state = store.getState();
  console.log('state ===> ', state);
});

I tried with a minimal implementation, here is the console result:

console.log src/Test.test.js:27
value BEFORE ====> 

console.log src/Test.test.js:35
value AFTER ====> My new value

console.log src/Test.test.js:38
state ===>  { user: { email: 'My new value' } }
Sign up to request clarification or add additional context in comments.

2 Comments

The change in state works fine, but value AFTER log still empty. I'm printing this.props.user.email inside of render from LoginScreen and still empty. But when i print store.getState() i can see the change
Works for me, if i call shallow again before wrapper.update().dive(); It seems like something should not be needed, do you have any idea why?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.