Enzyme can't shallow render because of an imported script using 'addEventListener'










1















Long story short, I have a React app that is importing a vanilla.js component. This component interacts with the DOM outside of the React app, but sometimes the React app needs to use the component. When I try and shallow render the React component that imports the Login component, Jest gets hung up on 'addEventListener' in the vanilla.js Login component:



TypeError: Cannot read property 'addEventListener' of null

90 | }
91 |
> 92 | document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
| ^
93 | document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
94 | };


So I have my react app:



import React from 'react';
import Login from '../login';
const Component = () =>
return (
<h3>
<span className='cc-stats-login-btn' onClick=(e) =>
Login.openRegister(e, 'Statistics')>Register</span>
or
<span className='cc-stats-login-btn' onClick=(e) => Login.openLogin(e, 'Statistics')>login</span>
to view advanced analytics and graphs
</h3>
);
;


The test for that app currently looks like:



document.body.innerHTML =
'<div class="login-modal-bg">' +
' <div class="login-modal-close"></div>' +
'</div>';

describe('<Component />', () =>
test('renders', () =>
const wrapper = shallow(
<Component />
);
expect(wrapper.exists()).toBe(true);
);
);


The js for the login component that Jest seems to be failing on:



const Login = (() => 
const config =
selectors:
loginModal: '.login-modal-bg',
loginClose: '.login-modal-close'

;

[...]

const initialize = () =>
document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
;
;


As far as I can tell based on Jest docs, all I need is a simple string as a 'mock dom' and the addEventListener should work. But all I get is the above TypeError. Any ideas?










share|improve this question



















  • 1





    Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

    – estus
    Nov 13 '18 at 19:15












  • I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

    – BrockBeldham
    Nov 13 '18 at 19:25











  • What was the result?

    – estus
    Nov 13 '18 at 19:26











  • always the same error: TypeError: Cannot read property 'addEventListener' of null

    – BrockBeldham
    Nov 13 '18 at 19:29






  • 1





    Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

    – estus
    Nov 13 '18 at 20:29















1















Long story short, I have a React app that is importing a vanilla.js component. This component interacts with the DOM outside of the React app, but sometimes the React app needs to use the component. When I try and shallow render the React component that imports the Login component, Jest gets hung up on 'addEventListener' in the vanilla.js Login component:



TypeError: Cannot read property 'addEventListener' of null

90 | }
91 |
> 92 | document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
| ^
93 | document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
94 | };


So I have my react app:



import React from 'react';
import Login from '../login';
const Component = () =>
return (
<h3>
<span className='cc-stats-login-btn' onClick=(e) =>
Login.openRegister(e, 'Statistics')>Register</span>
or
<span className='cc-stats-login-btn' onClick=(e) => Login.openLogin(e, 'Statistics')>login</span>
to view advanced analytics and graphs
</h3>
);
;


The test for that app currently looks like:



document.body.innerHTML =
'<div class="login-modal-bg">' +
' <div class="login-modal-close"></div>' +
'</div>';

describe('<Component />', () =>
test('renders', () =>
const wrapper = shallow(
<Component />
);
expect(wrapper.exists()).toBe(true);
);
);


The js for the login component that Jest seems to be failing on:



const Login = (() => 
const config =
selectors:
loginModal: '.login-modal-bg',
loginClose: '.login-modal-close'

;

[...]

const initialize = () =>
document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
;
;


As far as I can tell based on Jest docs, all I need is a simple string as a 'mock dom' and the addEventListener should work. But all I get is the above TypeError. Any ideas?










share|improve this question



















  • 1





    Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

    – estus
    Nov 13 '18 at 19:15












  • I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

    – BrockBeldham
    Nov 13 '18 at 19:25











  • What was the result?

    – estus
    Nov 13 '18 at 19:26











  • always the same error: TypeError: Cannot read property 'addEventListener' of null

    – BrockBeldham
    Nov 13 '18 at 19:29






  • 1





    Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

    – estus
    Nov 13 '18 at 20:29













1












1








1


0






Long story short, I have a React app that is importing a vanilla.js component. This component interacts with the DOM outside of the React app, but sometimes the React app needs to use the component. When I try and shallow render the React component that imports the Login component, Jest gets hung up on 'addEventListener' in the vanilla.js Login component:



TypeError: Cannot read property 'addEventListener' of null

90 | }
91 |
> 92 | document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
| ^
93 | document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
94 | };


So I have my react app:



import React from 'react';
import Login from '../login';
const Component = () =>
return (
<h3>
<span className='cc-stats-login-btn' onClick=(e) =>
Login.openRegister(e, 'Statistics')>Register</span>
or
<span className='cc-stats-login-btn' onClick=(e) => Login.openLogin(e, 'Statistics')>login</span>
to view advanced analytics and graphs
</h3>
);
;


The test for that app currently looks like:



document.body.innerHTML =
'<div class="login-modal-bg">' +
' <div class="login-modal-close"></div>' +
'</div>';

describe('<Component />', () =>
test('renders', () =>
const wrapper = shallow(
<Component />
);
expect(wrapper.exists()).toBe(true);
);
);


The js for the login component that Jest seems to be failing on:



const Login = (() => 
const config =
selectors:
loginModal: '.login-modal-bg',
loginClose: '.login-modal-close'

;

[...]

const initialize = () =>
document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
;
;


As far as I can tell based on Jest docs, all I need is a simple string as a 'mock dom' and the addEventListener should work. But all I get is the above TypeError. Any ideas?










share|improve this question
















Long story short, I have a React app that is importing a vanilla.js component. This component interacts with the DOM outside of the React app, but sometimes the React app needs to use the component. When I try and shallow render the React component that imports the Login component, Jest gets hung up on 'addEventListener' in the vanilla.js Login component:



TypeError: Cannot read property 'addEventListener' of null

90 | }
91 |
> 92 | document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
| ^
93 | document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
94 | };


So I have my react app:



import React from 'react';
import Login from '../login';
const Component = () =>
return (
<h3>
<span className='cc-stats-login-btn' onClick=(e) =>
Login.openRegister(e, 'Statistics')>Register</span>
or
<span className='cc-stats-login-btn' onClick=(e) => Login.openLogin(e, 'Statistics')>login</span>
to view advanced analytics and graphs
</h3>
);
;


The test for that app currently looks like:



document.body.innerHTML =
'<div class="login-modal-bg">' +
' <div class="login-modal-close"></div>' +
'</div>';

describe('<Component />', () =>
test('renders', () =>
const wrapper = shallow(
<Component />
);
expect(wrapper.exists()).toBe(true);
);
);


The js for the login component that Jest seems to be failing on:



const Login = (() => 
const config =
selectors:
loginModal: '.login-modal-bg',
loginClose: '.login-modal-close'

;

[...]

const initialize = () =>
document.querySelector(config.selectors.loginModal).addEventListener('click', (e) => closeLogin(e));
document.querySelector(config.selectors.loginClose).addEventListener('click', (e) => closeLogin(e));
;
;


As far as I can tell based on Jest docs, all I need is a simple string as a 'mock dom' and the addEventListener should work. But all I get is the above TypeError. Any ideas?







javascript reactjs jestjs enzyme






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 13 '18 at 20:33









skyboyer

3,75311229




3,75311229










asked Nov 13 '18 at 18:30









BrockBeldhamBrockBeldham

164




164







  • 1





    Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

    – estus
    Nov 13 '18 at 19:15












  • I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

    – BrockBeldham
    Nov 13 '18 at 19:25











  • What was the result?

    – estus
    Nov 13 '18 at 19:26











  • always the same error: TypeError: Cannot read property 'addEventListener' of null

    – BrockBeldham
    Nov 13 '18 at 19:29






  • 1





    Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

    – estus
    Nov 13 '18 at 20:29












  • 1





    Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

    – estus
    Nov 13 '18 at 19:15












  • I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

    – BrockBeldham
    Nov 13 '18 at 19:25











  • What was the result?

    – estus
    Nov 13 '18 at 19:26











  • always the same error: TypeError: Cannot read property 'addEventListener' of null

    – BrockBeldham
    Nov 13 '18 at 19:29






  • 1





    Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

    – estus
    Nov 13 '18 at 20:29







1




1





Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

– estus
Nov 13 '18 at 19:15






Testability is one of reasons why direct DOM access is not a good idea in React. I would suggest to mock document.querySelector calls instead of mocking DOM.

– estus
Nov 13 '18 at 19:15














I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

– BrockBeldham
Nov 13 '18 at 19:25





I agree that the direct DOM access isn't great in React. But due to far stranger business related decisions, it's not something I can do. I tried the following to mock document.querySelector and none of it works: document.querySelector = jest.fn(); global.document.querySelector = jest.fn(); global.document.querySelector.addEventListener = jest.fn();

– BrockBeldham
Nov 13 '18 at 19:25













What was the result?

– estus
Nov 13 '18 at 19:26





What was the result?

– estus
Nov 13 '18 at 19:26













always the same error: TypeError: Cannot read property 'addEventListener' of null

– BrockBeldham
Nov 13 '18 at 19:29





always the same error: TypeError: Cannot read property 'addEventListener' of null

– BrockBeldham
Nov 13 '18 at 19:29




1




1





Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

– estus
Nov 13 '18 at 20:29





Yes, that's an antipattern. I updated the answer. Since addEventListener doesn't even used in tested component, there's no need to even bother with it. Mock entire Login module and test it separately.

– estus
Nov 13 '18 at 20:29












1 Answer
1






active

oldest

votes


















1














Testing against DOM (it isn't real in Jest because it uses JSDOM) in unit tests provides additional moving parts. document.querySelector can be mocked instead:



const loginModalMock = jest.fn();
const loginCloseMock = jest.fn();

jest.spyOn(document, 'querySelector')
.mockReturnValue()
.mockReturnValueOnce( addEventListener: loginModalMock )
.mockReturnValueOnce( addEventListener: loginCloseMock )

const wrapper = shallow(
<Component />
);

...


In this specific case document.querySelector is used not in tested module. The module that uses it can be mocked at the top of test file:



jest.mock('.../login', () => ( openLogin: jest.fn() ));





share|improve this answer

























  • I believe Login should be better mocked even if it was used in Component.

    – skyboyer
    Nov 13 '18 at 20:44










Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













draft saved

draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287392%2fenzyme-cant-shallow-render-because-of-an-imported-script-using-addeventlistene%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














Testing against DOM (it isn't real in Jest because it uses JSDOM) in unit tests provides additional moving parts. document.querySelector can be mocked instead:



const loginModalMock = jest.fn();
const loginCloseMock = jest.fn();

jest.spyOn(document, 'querySelector')
.mockReturnValue()
.mockReturnValueOnce( addEventListener: loginModalMock )
.mockReturnValueOnce( addEventListener: loginCloseMock )

const wrapper = shallow(
<Component />
);

...


In this specific case document.querySelector is used not in tested module. The module that uses it can be mocked at the top of test file:



jest.mock('.../login', () => ( openLogin: jest.fn() ));





share|improve this answer

























  • I believe Login should be better mocked even if it was used in Component.

    – skyboyer
    Nov 13 '18 at 20:44















1














Testing against DOM (it isn't real in Jest because it uses JSDOM) in unit tests provides additional moving parts. document.querySelector can be mocked instead:



const loginModalMock = jest.fn();
const loginCloseMock = jest.fn();

jest.spyOn(document, 'querySelector')
.mockReturnValue()
.mockReturnValueOnce( addEventListener: loginModalMock )
.mockReturnValueOnce( addEventListener: loginCloseMock )

const wrapper = shallow(
<Component />
);

...


In this specific case document.querySelector is used not in tested module. The module that uses it can be mocked at the top of test file:



jest.mock('.../login', () => ( openLogin: jest.fn() ));





share|improve this answer

























  • I believe Login should be better mocked even if it was used in Component.

    – skyboyer
    Nov 13 '18 at 20:44













1












1








1







Testing against DOM (it isn't real in Jest because it uses JSDOM) in unit tests provides additional moving parts. document.querySelector can be mocked instead:



const loginModalMock = jest.fn();
const loginCloseMock = jest.fn();

jest.spyOn(document, 'querySelector')
.mockReturnValue()
.mockReturnValueOnce( addEventListener: loginModalMock )
.mockReturnValueOnce( addEventListener: loginCloseMock )

const wrapper = shallow(
<Component />
);

...


In this specific case document.querySelector is used not in tested module. The module that uses it can be mocked at the top of test file:



jest.mock('.../login', () => ( openLogin: jest.fn() ));





share|improve this answer















Testing against DOM (it isn't real in Jest because it uses JSDOM) in unit tests provides additional moving parts. document.querySelector can be mocked instead:



const loginModalMock = jest.fn();
const loginCloseMock = jest.fn();

jest.spyOn(document, 'querySelector')
.mockReturnValue()
.mockReturnValueOnce( addEventListener: loginModalMock )
.mockReturnValueOnce( addEventListener: loginCloseMock )

const wrapper = shallow(
<Component />
);

...


In this specific case document.querySelector is used not in tested module. The module that uses it can be mocked at the top of test file:



jest.mock('.../login', () => ( openLogin: jest.fn() ));






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 13 '18 at 20:27

























answered Nov 13 '18 at 19:38









estusestus

73.5k22108224




73.5k22108224












  • I believe Login should be better mocked even if it was used in Component.

    – skyboyer
    Nov 13 '18 at 20:44

















  • I believe Login should be better mocked even if it was used in Component.

    – skyboyer
    Nov 13 '18 at 20:44
















I believe Login should be better mocked even if it was used in Component.

– skyboyer
Nov 13 '18 at 20:44





I believe Login should be better mocked even if it was used in Component.

– skyboyer
Nov 13 '18 at 20:44



















draft saved

draft discarded
















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53287392%2fenzyme-cant-shallow-render-because-of-an-imported-script-using-addeventlistene%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kleinkühnau

Makov (Slowakei)

Deutsches Schauspielhaus