CodeSeven / toastr

Simple javascript toast notifications
http://www.toastrjs.com
MIT License
11.99k stars 2.04k forks source link

TypeError: $.extend is not a function #559

Closed Ahershel closed 6 years ago

Ahershel commented 6 years ago

Hi, i just use toastr for a few days. I have problem when testing my action (react). Here's the error:

TypeError: $.extend is not a function
      at getOptions (node_modules/toastr/toastr.js:452:26)
      at Object.error (node_modules/toastr/toastr.js:49:32)
      at static/js/actions/action_coa.js:17:14
      at <anonymous>

I'm using mocha, chai and nock Here's my test code:

it('should create a FETCH_ALL_COA action', () => {
    nock(`http://${ROOT_URL}`)
      .get('api/coa/?format=json')
      .reply(200, dummy_coa);

    const store = mockStore({ coa: {} });
    let expectedAction = [{ type: FETCH_ALL_COA, payload: [{
      ...dummy_coa
    }]}];
    return store.dispatch(fetchAllCOA())
      .then(() => {
        expect(store.getActions()).to.eql(expectedAction);
      });
  });

beside this error, it also give a warning on my console (testing result ):

(node:2758) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 745): TypeError: $.extend is not a function

my action code:

import axios from 'axios';
import {ROOT_URL, FETCH_ALL_COA, FETCH_COA, DELETE_COA} from './types.js';
import toastr from '../components/my_toastr.js';

export function fetchAllCOA(token){
  const url = `${ROOT_URL}api/coa/?format=json`;
  const request = axios.get(url, {headers:{Authorization:token}});
  return (dispatch => {
    return request.then(data => {
      dispatch({type: FETCH_ALL_COA, payload: data.data});
    }).catch(() => {
      toastr.error('Can\'t connect to server!<br />' + 
        'Please check your internet connection', 'Error!');
    });
  });
}

Pardon my English, it's not my native language.

Please help me, wether i made some mistakes or did i miss something. Thank you

john-sanders-2 commented 6 years ago

$.extend is a jQuery function. toastr depends on jQuery. Are you loading it before toastr?

Ahershel commented 6 years ago

yes, i import the jquery in my test helper:

import { JSDOM } from 'jsdom';
import jQuery from 'jquery';
import chai, { expect } from 'chai';
import chaiJquery from 'chai-jquery';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import reducers from '../js/reducers/index.js';
import { MemoryRouter } from 'react-router-dom';
import { createMockStore } from 'redux-test-utils';

// Emulate browser in the terminal
const dom = new JSDOM('<!doctype html><html><body></body></html>');
global.document = dom.window.document;
global.window = global.document.defaultView;
global.navigator = {
  userAgent: 'node.js'
};

var React = require('react');
var ReactDOM = require('react-dom');
var ReactTestUtils = require('react-dom/test-utils');

const $ = jQuery(window);

// Setup chaiJquery
chaiJquery(chai, chai.util, $);
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
// Helper function to render React Component/Container to return jQuery wrapped
// object.
function renderComponent(ComponentClass, props = {}, state = {}) {
  const componentInstance = ReactTestUtils.renderIntoDocument(
    <Provider store={createStoreWithMiddleware(reducers, state)}>
      <MemoryRouter>
        <ComponentClass {...props} />
      </MemoryRouter>
    </Provider>
  );

  return $(ReactDOM.findDOMNode(componentInstance));
}

// Helper function to simulate event on the renderComponent
$.fn.simulate = function (eventName, value) {
  if (value) {
    this.val(value);
  }
  ReactTestUtils.Simulate[eventName](this[0]);
};

export { renderComponent, expect };

the toastr is imported in my action:

import toastr from '../components/my_toastr.js';

my_toastr.js:

import toastr from 'toastr';

toastr.options = {
  'closeButton': true,
  'debug': false,
  'newestOnTop': true,
  'progressBar': false,
  'positionClass': 'toast-top-right',
  'preventDuplicates': true,
  'onclick': null,
  'showDuration': '300',
  'hideDuration': '1000',
  'timeOut': '0',
  'extendedTimeOut': '1000',
  'showEasing': 'swing',
  'hideEasing': 'linear',
  'showMethod': 'fadeIn',
  'hideMethod': 'fadeOut'
};

export default toastr;
john-sanders-2 commented 6 years ago

This doesn't sound like a toastr problem to me. Have you tried a simple script that imports jQuery and toastr then creates a toast?

Ahershel commented 6 years ago

I found the solution by doing this: in toastr.js (node_modules/toastr) I change the extend to Object.assign :

function getOptions() {
   return Object.assign({}, getDefaults(), toastr.options);
}
shubmittal commented 6 years ago

Did you find a way to make it work in repeatable manner such that anyy other team member, who does npm install won't have this change manually? I am facing the same issue and your solution is the only one that's relevant to my scenario.

menelaosbgr commented 6 years ago

Is there a way to do this correctly? I am also getting an error for this!

elvismercado commented 6 years ago

I am also experiencing this with fullcalendar. just changing the $.extend to Object.assing doesn't seem like the best solution