Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React component using jQuery without require - jest unit tests

I have a very simple React mixin which uses jQuery to trigger an event

MyMixin = {
  trackStructEvent: function () {
    args = Array.prototype.slice.call(arguments);
    $('body').trigger('myEvent', args);
  }
module.exports = MyMixin

This is imported into the main site as part of a new set of components using browserify. As the main site holding these components will always include jQuery, I don't want to require jQuery with browserify, as it will be duplicated.

This isn't an issue in terms of behaviour - however it causes problems when running jest to unit test the components using this mixin throwing the error.

ReferenceError: $ is not defined

I know I can fix this by including jQuery through browserify, but that will load 2 copies into my site.

Is there any way in jest to tell my react component that jQuery already exists on the window and not to worry about it?

like image 827
Sam Ternent Avatar asked Nov 28 '14 13:11

Sam Ternent


People also ask

Do you need Jest for React testing library?

Some familiarity with Jest as a test runner or framework is helpful but not required. Because Jest is pre-packaged with Create React App, you do not need to install it separately.

Do I need to install Jest in React app?

json, and you will find that when you use create-react-app for creating a react project, it has default support for jest and react testing library. This means that we do not have to install them manually.

What is the difference between Jest and React testing library?

Jest provides a great iteration speed combined with powerful features like mocking modules and timers so you can have more control over how the code executes. React Testing Library is a set of helpers that let you test React components without relying on their implementation details.

Why we use Jest with React testing library?

Jest is a test runner that finds tests, runs the tests, and determines whether the tests passed or failed. Additionally, Jest offers functions for test suites, test cases, and assertions. React Testing Library provides virtual DOMs for testing React components.


3 Answers

I have a similar problem but I think where i've got to so far solves your problem. You need to use require inside your react files to define jQuery under $

var $ = require('jquery');

MyMixin = {
   trackStructEvent: function () {
      args = Array.prototype.slice.call(arguments);
      $('body').trigger('myEvent', args);
   }
module.exports = MyMixin

You then need to tell jest not to mock jquery

jest.dontMock('jquery')

Then your jest unit tests should pass (assuming they aren't verifying things that jQuery is doing - this is where my tests are falling over).

You also need to tell browserify that jQuery is external then the result will be that all your references to $ have been defined, the jquery library is loaded for testing but is not included in your browserify bundle. (use browserify-shim)

like image 140
brommersman Avatar answered Oct 18 '22 15:10

brommersman


What I'm going with is:

if (process.env.NODE_ENV === 'test') window.$ = require('jquery');

I hope this helps someone!

UPDATE:

I've begun to import $ from 'jquery' in all React files that use jQuery. This removes the dependency on window.$ being the type of jQuery I expect (I have run into problems with other libraries messing with window.$. At first, I was concerned about importing jQuery everywhere, because I was afraid that this would increase my bundle size. But the way bundling works, all these imports will point to a singleton, so bundle size would not increase.

like image 20
Zachary Ryan Smith Avatar answered Oct 18 '22 15:10

Zachary Ryan Smith


For my part, I am not interested in testing the jQuery functionality in my components. In addition, I have not included jQuery in my bundle because it is used and included all over the website my app is on. So I have, for example, done the following:

// SubmitRender.js

// ...import statements...

var SubmitRender = React.createClass({
  componentWillMount: function () {
    $('form').on('submit', (e)=>{
      this.handleSubmit(e);
    });
  },

  // ...more code...
});
export default SubmitRender;

.

// SubmitRender.spec.js

// ...import statements...

describe('SubmitRender', () => {
  beforeAll(() => {
    // mocking jQuery $()
    global.window.$ = jest.fn(() => {return {on: jest.fn()}});
  });

  it('renders without error', () => {
    expect( () => render(<SubmitRender />) ).not.toThrow();
  });
});

I know my component will not mount without calling the $ function. So I just mock out that function with jest.fn in a beforeAll(). Now, I also know that my jQuery function inside my actual component is chained. So I know that I also need to account for jQuery's .on(), so I make sure to return an object with an on function. There is no further chaining, so that is where I can stop.

If I had a more complex set of chained functions, I could do something like this:

beforeAll(() => {
  // mocking jQuery $()
  var fakeQuery = jest.fn(() => {
    return {
      on: fakeQuery,
      off: fakeQuery,
      click: fakeQuery
    }
  })
  global.window.$ = fakeQuery;
});
like image 43
Don Avatar answered Oct 18 '22 15:10

Don