question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Offer sync rendering for testing purposes

See original GitHub issue

I’m trying to test my implementations of toasts, namely the rendering (using snapshots but this is not especially relevant). The problem is that if i call the toast function in my test then render the output the toasts are not present in the output. I believe this might be because the library is event driven and so async (though could be wrong). What i would like is access to an API that allows me to test the rendered output of the call to toast synchronously. Because toast exports only a function (not React components) its not clear how this can be achieved with the library currently. Could you consider either a) documenting how this can be achieved with the existing API without hacks such as setTimeout being required in tests, or b) update the API / library to provide some test utils for testing notification rendering synchronously?

Here is my test implementation. Note the snapshots are written using chai-jest-snapshot

Sync tests

// Test spec
it('correctly renders a success notification', () => {
    class App extends Component {
      notify = () => {
        toast("Wow so easy !")
      };
      render(){
        return (
          <div>
            {this.notify()}
            <ToastContainer />
          </div>
        );
      }
    }

    wrapper = mount(<App />);
    expect(toJson(wrapper)).to.matchSnapshot();
  });

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`correctly renders a success notification 1`] = `
<App>
  <div>
    <ToastContainer
      autoClose={5000}
      bodyClassName={null}
      className={null}
      closeButton={
        <CloseButton
          ariaLabel="close"
        />
      }
      closeOnClick={true}
      draggable={true}
      draggablePercent={80}
      hideProgressBar={false}
      newestOnTop={false}
      pauseOnHover={true}
      pauseOnVisibilityChange={true}
      position="top-right"
      progressClassName={null}
      rtl={false}
      style={null}
      toastClassName={null}
      transition={[Function]}
    >
      <div
        className="Toastify"
      />
    </ToastContainer>
  </div>
</App>
`;

Async tests

// Test spec
 it('correctly renders a success notification', () => {
    class App extends Component {
      notify = () => {
        toast("Wow so easy !")
      };
      render(){
        return (
          <div>
            {this.notify()}
            <ToastContainer />
          </div>
        );
      }
    }

    wrapper = mount(<App />);
  
    // Note async - not feasible real-world solution
    setTimeout(() => {
      wrapper.update();
      expect(toJson(wrapper)).to.matchSnapshot();
    }, 5);
  });

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`correctly renders a warning notification 1`] = `
<App>
  <div>
    <ToastContainer
      autoClose={5000}
      bodyClassName={null}
      className={null}
      closeButton={
        <CloseButton
          ariaLabel="close"
        />
      }
      closeOnClick={true}
      draggable={true}
      draggablePercent={80}
      hideProgressBar={false}
      newestOnTop={false}
      pauseOnHover={true}
      pauseOnVisibilityChange={true}
      position="top-right"
      progressClassName={null}
      rtl={false}
      style={null}
      toastClassName={null}
      transition={[Function]}
    >
      <div
        className="Toastify"
      >
        <TransitionGroup
          childFactory={[Function]}
          className="Toastify__toast-container Toastify__toast-container--top-right"
          component="div"
          key="container-top-right"
          style={Object {}}
        >
          <div
            className="Toastify__toast-container Toastify__toast-container--top-right"
            style={Object {}}
          >
            <Toast
              autoClose={5000}
              bodyClassName={null}
              className={null}
              closeButton={
                <CloseButton
                  ariaLabel="close"
                  closeToast={[Function]}
                  type="default"
                />
              }
              closeOnClick={true}
              closeToast={[Function]}
              draggable={true}
              draggablePercent={80}
              hideProgressBar={false}
              id={1}
              in={true}
              isDocumentHidden={false}
              key=".$toast-1"
              onClose={[Function]}
              onExited={[Function]}
              onOpen={[Function]}
              pauseOnHover={true}
              position="top-right"
              progressClassName={null}
              role="alert"
              rtl={false}
              transition={[Function]}
              type="default"
              updateId={null}
            >
              <Animation
                appear={true}
                in={true}
                onExited={[Function]}
                position="top-right"
                preventExitTransition={false}
                unmountOnExit={true}
              >
                <Transition
                  appear={true}
                  enter={true}
                  exit={true}
                  in={true}
                  mountOnEnter={false}
                  onEnter={[Function]}
                  onEntered={[Function]}
                  onEntering={[Function]}
                  onExit={[Function]}
                  onExited={[Function]}
                  onExiting={[Function]}
                  timeout={
                    Object {
                      "enter": 750,
                      "exit": 750,
                    }
                  }
                  unmountOnExit={true}
                >
                  <div
                    className="Toastify__toast Toastify__toast--default"
                    onClick={[Function]}
                    onMouseDown={[Function]}
                    onMouseEnter={[Function]}
                    onMouseLeave={[Function]}
                    onTouchStart={[Function]}
                    onTransitionEnd={[Function]}
                  >
                    <div
                      className="Toastify__toast-body"
                      role="alert"
                    >
                      Wow so easy !
                    </div>
                    <CloseButton
                      ariaLabel="close"
                      closeToast={[Function]}
                      type="default"
                    >
                      <button
                        aria-label="close"
                        className="Toastify__close-button Toastify__close-button--default"
                        onClick={[Function]}
                        type="button"
                      >
                        ✖
                      </button>
                    </CloseButton>
                    <ProgressBar
                      className={null}
                      closeToast={[Function]}
                      delay={5000}
                      hide={false}
                      isRunning={true}
                      rtl={false}
                      type="default"
                    >
                      <div
                        className="Toastify__progress-bar Toastify__progress-bar--default"
                        onAnimationEnd={[Function]}
                        style={
                          Object {
                            "animationDuration": "5000ms",
                            "animationPlayState": "running",
                            "opacity": 1,
                          }
                        }
                      />
                    </ProgressBar>
                  </div>
                </Transition>
              </Animation>
            </Toast>
          </div>
        </TransitionGroup>
      </div>
    </ToastContainer>
  </div>
</App>
`;

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:11 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
rohan-naikcommented, Jun 9, 2020

Hello, I’ve made a common component sort of thing for Rendering the toast. This is what it returns.

<>
     {toast.error(displayToast,{
           hideProgressBar: hideProgressBar,
          position: position,
          closeOnClick:closeOnClick,
          draggable:draggable
     })}
     <ToastContainer/>
</>

I’m trying to write test cases for the above snippet where I just run the method that triggers the Toast

e.g:- Toast() will trigger the above function.

I’m testing it using ReactTestUtils and jest .

The test that I have written is :

describe('Notification', () => {
    it('should render Default Notification', () => {
        const result = Toast({displayContent:{text:'Without ProgressBar'},notifyProps:{ hideProgressBar:true },type:'default'})
        const tree = renderer.create(result);
        console.log(tree.toJSON());
    })
})

and when I try console log the tree.toJSON() this is what it returns

[
      'qcwt2sybdd',
      {
        type: 'div',
        props: { className: 'Toastify', id: undefined },
        children: null
      }
    ]

How do I test what is being rendered in the toast? Or what is the text that toast renders?

1reaction
fkhadracommented, Jul 2, 2018

Indeed, the unmount should reset the id of the toast, at least this is what I plan to do 😁.

Also, notice that with the following implementation, your are calling toast before the ToastContainer is mounted(first route):

render(){
        return (
          <div>
            {this.notify() /* same as doing toast('hello')*/}
            <ToastContainer />
          </div>
        );
      }

In that case, when the container is not mounted the notification are stacked and rendered as soon as the container is mounted. This explain the inconsistency for your test regardless the toastId issue.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React's sync and async `act`. One challenge of ... - Medium
When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface....
Read more >
Testing-library: avoid these mistakes in async tests
In our test, when we are calling render with await , JavaScript implicitly wraps the result into a promise and waits for it...
Read more >
Understanding Act function | React Native Testing Library
The responsibility for act function is to make React renders and updates work in tests in a similar way they work in real...
Read more >
How to Test React Components: the Complete Guide
Mount/render is typically used for integration testing and shallow is used for unit testing. shallow rendering only renders the single component ...
Read more >
Testing: Helpers - Open Web Components
Testing helpers uses lit, but it's set up as a peer dependency to avoid ... A test fixture renders a piece of HTML...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found