0

**How to check for the dynamic state changes in a parent component and write the test case using the react testing library if the props passed to the child component are based on the state changes which are happening dynamically in the parent component. Can someone help me with this?enter image description here

App.js

    import React, { Component } from 'react';
    import './App.css';
    import TextArea from './components/TextArea/TextArea';
    
    class App extends Component {
      constructor() {
        super();
        this.state = {
          textAreaParams: {},
        };
      }
      componentDidMount() {
        this.setDefaultTextAreaMessage();
      }
      setDefaultTextAreaMessage = () => {
        this.setState({
          textAreaParams: { message: 'Hello' },
        });
      };
      render() {
        const { textAreaParams } = this.state;
        return (
          <div className="App">
            {Object.keys(textAreaParams).length > 0 ? (
              <TextArea params={textAreaParams} />
            ) : null}
          </div>
        );
      }
    }
    
    export default App;

TextArea.js

    import { Component } from 'react';
    
    class TextArea extends Component {
      constructor(props) {
        super(props);
        this.state = {
          message: this.props.params.message,
        };
      }
      render() {
        return (
          <div>
            <textarea
              rows="4"
              cols="50"
              value={this.state.message ? this.state.message : ''}
              placeholder="test"
              onChange={() => {}}
            >
              {this.state.message}
            </textarea>
          </div>
        );
      }
    }
    
    export default TextArea;
   

App.test.js

    import App from './App';
    import { cleanup, render } from '@testing-library/react';
    
    describe('Rendering the App component and passing props to text area', () => {
      afterEach(cleanup);
      it('render the App component and check for the TextArea component', async () => {
        const props = { textAreaParams: { message: 'save' } };
        const { getByPlaceholderText } = render(<App {...props} />);
        const textAreaParams = getByPlaceholderText('test');
        expect(textAreaParams).toHaveTextContent('save');
      });
    });
  • Why do you need to mock anything? If your test is for the App you definitely shouldn't mock it out (then you're just testing a mock) and if you want to test the TextArea in isolation you can already do so, your test effectively becomes the parent. – jonrsharpe Jul 25 '21 at 07:40
  • Hi @jhonrsharpe thanks for the quick response but the question I am trying to ask is incase if you have a child component which depends on the value which you pass from parent how do you test it. I had just given a small example with app (parent) and textarea (child) – Murugesh Aravind Jul 25 '21 at 07:43
  • Your test doesn't work because App doesn't take any props, yet you try to pass some anyway. It's not clear why you'd expected that to work. Again you can either test TextArea in isolation, where you can pass props directly to it, or in the context of App, where in this case it will always pass what's hard-coded in that component. – jonrsharpe Jul 25 '21 at 07:48
  • do you think below one as an ideal approach – Murugesh Aravind Jul 25 '21 at 08:05
  • it('check the text area params', () => { const params = { message: 'h1' }; const { getByPlaceholderText } = render(); const textAreaParams = getByPlaceholderText('test'); expect(textAreaParams).toHaveTextContent('h1'); });` – Murugesh Aravind Jul 25 '21 at 08:05

1 Answers1

1

We need to pass onChange handler prop from the App component to TextArea and then TextArea will component will call that handler when there is a change in the text area.

updateTextAreaMessage = (messageInTextArea) => {
  this.setState({
    textAreaParams: { message: messageInTextArea}
  })
}

In the above code, messageInTextArea is a string value when we change the text in TextArea and when updateTextAreaMessage is called in the TextArea component with the same string value as a parameter, it will update the state in the App component.

Full Implementation:

App.js:

import React, { Component } from "react";
import './App.css';
import TextArea from './components/TextArea/TextArea';

class Main extends Component {
  constructor() {
    super();
    this.state = {
      textAreaParams: { message: "hello" } // we can provide default value here
    };
  }

  updateTextAreaMessage = (messageInTextArea) => {
    this.setState({
      textAreaParams: { message: messageInTextArea }
    });
  };

  render() {
    const { textAreaParams } = this.state;
    return (
      <div className="App">
        {Object.keys(textAreaParams).length > 0 ? (
          <TextArea
            params={textAreaParams}
            onUpdate={this.updateTextAreaMessage}
          />
        ) : null}

        <p aria-label="text area message">{textAreaParams.message}</p>
      </div>
    );
  }
}

export default Main;

TextArea.js:

import { Component } from "react";

class TextArea extends Component {
  render() {
    return (
      <div>
        <textarea
          rows="4"
          cols="50"
          value={this.props.params.message ? this.props.params.message : ""}
          placeholder="test"
          onChange={(event) => this.props.onUpdate(event.target.value)}
        >
          {this.props.params.message}
        </textarea>
      </div>
    );
  }
}

export default TextArea;

Now, we'll add the test for App.js. But the question is what to test here? The answer would we'll add the test for whether the state is updated or not when there is a change in the text of the TextArea component.
import { render } from "@testing-library/react";
import App from "./App";
import TextArea from './components/TextArea/TextArea';

describe("Rendering the App component and passing props to text area", () => {
  it("should render the App component with default message in TextArea", () => {
    const { getByPlaceholderText } = render(<Main />);
    const textAreaParams = getByPlaceholderText("test");
    expect(textAreaParams).toHaveTextContent(/hello/i);
  });

  it("should update the text area when we type something", () => {
    const { getByPlaceholderText, getByLabelText } = render(<Main />);
    userEvent.type(getByPlaceholderText("test"), "Anything");
    expect(getByLabelText(/text area message/i)).toHaveTextContent(/anything/i);
  });
});

describe("Rendering the Text Area component", () => {
  it("should render the TextArea component and calls onChange handlers when we type something", () => {
    const mockOnChangeHandler = jest.fn();
    const mockParams = { message: "save" };
    const { getByPlaceholderText } = render(
      <TextArea params={mockParams} onUpdate={mockOnChangeHandler} />
    );
    const inputTextArea = getByPlaceholderText("test");
    expect(inputTextArea).toHaveTextContent(/save/i);

    userEvent.type(inputTextArea, "Anything");
    expect(mockOnChangeHandler).toHaveBeenCalled();
  });
});

Subrato Pattanaik
  • 5,331
  • 6
  • 21
  • 52