1

I want to test that user should not navigate to any other screen if TextInput equals to "".

I have tried to put console logs to understand more correctly but the problem is now that, after input.simulate("changeText", "[email protected]"); happened correctly, console log says that incoming value from test is "[email protected]" but after setValue(text); happened console.log says that useState value is "".

Why state is not changing? Do I need to update the wrapper with .update() ?

Here is my React Native code:

import React, { useState } from "react";
import { View, Button, TextInput } from "react-native";

export const LoginContainer = (props) => {
  const [value, setValue] = useState("");

  const handleClick = () => {
    if (value !== "") {
      props.navigation.navigate("any other screen");
    }
  }

  return (
    <View>
      <TextInput
        test-id="login-input"
        onChangeText={(text) => {
          console.log("INPUT_TEXT:", text);
          setValue(text);
          console.log("STATE_TEXT:", value);
        }}
        value={value}
      />
      <Button test-id="login-button" onPress={() => handleClick()} />
    </View>
  );
}

Console log:

  console.log
    INPUT_TEXT: [email protected]

      at onChangeText (....)

  console.log
    STATE_TEXT: 

      at onChangeText (....)

  expect(jest.fn()).toHaveBeenCalledTimes(expected)

  Expected number of calls: 1
  Received number of calls: 0

Test code:

import "react-native";
import React from "react";
import { shallow } from 'enzyme';
import { LoginContainer } from "...";
import { findByTestAttr } from '...';

const navigation = {
  navigate: jest.fn()
}

describe('correct login action', () => {
    const wrapper = shallow(<LoginContainer navigation={navigation} />);
    let input = findByTestAttr(wrapper, "login-input");
    let button = findByTestAttr(wrapper, "login-button");

    test('should not navigate to login mail screen if email adress is not entered', () => {
      input.simulate("changeText", "[email protected]");
      button.simulate("press");

      expect(navigation.navigate).toHaveBeenCalledTimes(1);

      //input.simulate("changeText", "");
      //button.simulate("press");
      //expect(navigation.navigate).toHaveBeenCalledTimes(0);
    });
});

2 Answers 2

0

Hello setValue is an asynchronous function so it doesn't update the state immediately, you can see the next value until the next rendering

add a console.log like below

import React, { useState } from "react";
import { View, Button, TextInput } from "react-native";

export const LoginContainer = (props) => {
  const [value, setValue] = useState("");

  const handleClick = () => {
    if (value !== "") {
      props.navigation.navigate("any other screen");
    }
  }
 // try to add console.log here and you will see the new value after click
 console.log("STATE_TEXT:", value);

  return (
    <View>
      <TextInput
        test-id="login-input"
        onChangeText={(text) => {
          console.log("INPUT_TEXT:", text);
          setValue(text);
        }} 
      // if you want the setValue to be executed immediately you can call it inside promise like below, however the value will still the same even after re-rendering
        //onChangeText={(text) => {
          //console.log("INPUT_TEXT:", text);
          //Promise.resolve().then(() => {
            //setValue(text);
            //console.log("STATE_TEXT after calling it inside promise:", value);
          //});
          //console.log("STATE_TEXT:", value);
        //});
        value={value}
      />
      <Button test-id="login-button" onPress={() => handleClick()} />
    </View>
  );
}

By default react re-render the component at the end of a react event handler. For more information about how react update state please check this blog here

Sign up to request clarification or add additional context in comments.

4 Comments

I already know how it works, unfortunately this is not a solution for me. I'm just looking for a solution similar to await or Promise to wait and render like that
I did an update, so to conclude you can force setValue to be executed however the value inside the event handler will not change.
I just solved this issue @ARZMI Imad and your information may be helper but not a right solution for this type of problem. Please check my answer. It's just needed to be updated in testing file.
welcome, but sorry to say that you're question were not clear, I just realize it from you're anwser.
0

I solved this ussie with the solution in here.

test('should not navigate to login mail screen if email adress is not entered', () => {
    const wrapper = shallow(<LoginContainer navigation={navigation} />);
    input = findByTestAttr(wrapper, "login-input");

    input.simulate("changeText", "[email protected]");
    wrapper.update(); // update after changing the state
   
    // After performing update, should find button element
    button = findByTestAttr(wrapper, "login-button");
    button.simulate("press");
    expect(navigation.navigate).toHaveBeenCalledTimes(1);
});

Comments

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.