schiehll / react-alert

alerts for React
MIT License
608 stars 98 forks source link

Difficulty running unit tests for component with alerts #148

Closed fgs-dbudwin closed 4 years ago

fgs-dbudwin commented 4 years ago

I have a simple component that uses react-alert to display a "Hello World!" message. I am using React 16.12.0, jest 25.1.0, and react-alert 6.0.0. I wanted to write unit tests around my form using Jest, but I got the following error:

 FAIL  frontend/src/components/clients/__test__/hello-world.test.tsx
  ● Console

    console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
      Error: Uncaught [TypeError: Cannot read property 'current' of undefined]
          at reportException (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\helpers\runtime-script-errors.js:62:24)
          at innerInvokeEventListeners (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:332:9)
          at invokeEventListeners (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:267:3)
          at HTMLUnknownElementImpl._dispatch (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:214:9)
          at HTMLUnknownElementImpl.dispatchEvent (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:87:17)
          at HTMLUnknownElement.dispatchEvent (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\generated\EventTarget.js:144:23)
          at Object.invokeGuardedCallbackDev (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:385:16)
          at invokeGuardedCallback (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:440:31)
          at beginWork$$1 (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:25780:7)
          at performUnitOfWork (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24698:12) TypeError: Cannot read property 'current' of undefined
          at Object.useAlert (C:\dev\my_project\node_modules\react-alert\dist\cjs\react-alert.js:493:23)
          at HelloWorld (C:\dev\my_project\frontend\src\components\clients\hello-world.tsx:6:19)
          at renderWithHooks (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:16260:18)
          at mountIndeterminateComponent (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:18794:13)
          at beginWork$1 (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:20162:16)
          at HTMLUnknownElement.callCallback (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:336:14)
          at innerInvokeEventListeners (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:316:27)
          at invokeEventListeners (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:267:3)
          at HTMLUnknownElementImpl._dispatch (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:214:9)
          at HTMLUnknownElementImpl.dispatchEvent (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\events\EventTarget-impl.js:87:17)
          at HTMLUnknownElement.dispatchEvent (C:\dev\my_project\node_modules\jsdom\lib\jsdom\living\generated\EventTarget.js:144:23)
          at Object.invokeGuardedCallbackDev (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:385:16)
          at invokeGuardedCallback (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:440:31)
          at beginWork$$1 (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:25780:7)
          at performUnitOfWork (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24698:12)
          at workLoopSync (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24671:22)
          at performSyncWorkOnRoot (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24270:11)
          at scheduleUpdateOnFiber (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:23698:7)
          at updateContainer (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:27103:3)
          at C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:27528:7
          at unbatchedUpdates (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24433:12)
          at legacyRenderSubtreeIntoContainer (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:27527:5)
          at Object.render (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:27608:10)
          at test_utils_1.act (C:\dev\my_project\frontend\src\components\clients\__test__\hello-world.test.tsx:22:9)
          at batchedUpdates$1 (C:\dev\my_project\node_modules\react-dom\cjs\react-dom.development.js:24386:12)
          at Object.act (C:\dev\my_project\node_modules\react-dom\cjs\react-dom-test-utils.development.js:1092:14)
          at Object.<anonymous>.it (C:\dev\my_project\frontend\src\components\clients\__test__\hello-world.test.tsx:21:5)
          at Object.asyncJestTest (C:\dev\my_project\node_modules\jest-jasmine2\build\jasmineAsyncInstall.js:100:37)
          at resolve (C:\dev\my_project\node_modules\jest-jasmine2\build\queueRunner.js:43:12)
          at new Promise (<anonymous>)
          at mapper (C:\dev\my_project\node_modules\jest-jasmine2\build\queueRunner.js:26:19)
          at promise.then (C:\dev\my_project\node_modules\jest-jasmine2\build\queueRunner.js:73:41)
          at processTicksAndRejections (internal/process/task_queues.js:86:5)
    console.error node_modules/react-dom/cjs/react-dom.development.js:21843
      The above error occurred in the <HelloWorld> component:
          in HelloWorld

      Consider adding an error boundary to your tree to customize error handling behavior.
      Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

  ● renders without crashing

    TypeError: Cannot read property 'current' of undefined

      4 |
      5 | export default function HelloWorld() {
    > 6 |     const alert = useAlert();
        |                   ^
      7 |
      8 |     return (
      9 |         <div>{alert.error("Hello World!")}</div>

      at Object.useAlert (node_modules/react-alert/dist/cjs/react-alert.js:493:23)
      at HelloWorld (frontend/src/components/clients/hello-world.tsx:6:19)
      at renderWithHooks (node_modules/react-dom/cjs/react-dom.development.js:16260:18)
      at mountIndeterminateComponent (node_modules/react-dom/cjs/react-dom.development.js:18794:13)
      at beginWork$1 (node_modules/react-dom/cjs/react-dom.development.js:20162:16)
      at HTMLUnknownElement.callCallback (node_modules/react-dom/cjs/react-dom.development.js:336:14)
      at innerInvokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:316:27)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:267:3)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:214:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)
      at Object.invokeGuardedCallbackDev (node_modules/react-dom/cjs/react-dom.development.js:385:16)
      at invokeGuardedCallback (node_modules/react-dom/cjs/react-dom.development.js:440:31)
      at beginWork$$1 (node_modules/react-dom/cjs/react-dom.development.js:25780:7)
      at performUnitOfWork (node_modules/react-dom/cjs/react-dom.development.js:24698:12)
      at workLoopSync (node_modules/react-dom/cjs/react-dom.development.js:24671:22)
      at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:24270:11)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:23698:7)
      at updateContainer (node_modules/react-dom/cjs/react-dom.development.js:27103:3)
      at node_modules/react-dom/cjs/react-dom.development.js:27528:7
      at unbatchedUpdates (node_modules/react-dom/cjs/react-dom.development.js:24433:12)
      at legacyRenderSubtreeIntoContainer (node_modules/react-dom/cjs/react-dom.development.js:27527:5)
      at Object.render (node_modules/react-dom/cjs/react-dom.development.js:27608:10)
      at test_utils_1.act (frontend/src/components/clients/__test__/hello-world.test.tsx:22:9)
      at batchedUpdates$1 (node_modules/react-dom/cjs/react-dom.development.js:24386:12)
      at Object.act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:1092:14)
      at Object.<anonymous>.it (frontend/src/components/clients/__test__/hello-world.test.tsx:21:5)

My React component is quite simple (hello-world.tsx):

import React from "react";

import { useAlert } from "react-alert";

export default function HelloWorld() {
    const alert = useAlert();

    return (
        <div>{alert.error("Hello World!")}</div>
    );
}

My unit test is quite simple (hello-world.test.tsx):

import React from "react";
import { render, unmountComponentAtNode } from "react-dom";
import { act } from "react-dom/test-utils";

import HelloWorld from "../hello-world";

let container: HTMLDivElement = null;

beforeEach(() => {
    container = document.createElement("div");
    document.body.appendChild(container);
});

afterEach(() => {
    unmountComponentAtNode(container);
    container.remove();
    container = null;
});

it("renders without crashing", () => {
    act(() => {
        render(<HelloWorld />, container);
    });
});

If I modify my HelloWorld component to be the following, my unit test will pass.

import React from "react";

export default function HelloWorld() {
    return (
        <div>"Hello World!"</div>
    );
}

Any advice as to what could be going wrong here? I am relatively new to this ecosystem, so I may likely be overlooking something.

besLisbeth commented 4 years ago

Hi @fgs-dbudwin, did you find a workaround? I think, that you have a wrong approach to call an alert. Did you try to call an alert.error on button click like in the Usage section in the documentation, simulate a click and check if alert appears on the screen?

JostHren commented 4 years ago

Try to add the following imports to test file: import { Provider as AlertProvider } from "react-alert"; import AlertTemplate from "react-alert-template-basic";

... And wrap your HelloWorld component in test file like so: render(<AlertProvider template={AlertTemplate}><HelloWorld /></AlertProvider>, container);

I don't know if this is the right or good solution. Maybe it is stupid and wrong. But it works for me.

schiehll commented 4 years ago

@JostHren is right, you can only use the useAlert hook if the component using it is a child of an AlertProvider, otherwise, it wouldn't have the context to work.