JestJs thinks ajax is not called - javascript

I'm having a lot of trouble getting a simple jest test to work. Jest insists that my Ajax call is not happening, with the error message:
FAIL authTest.js (1.828s)
● Authentication: Logging In › it Doesn't currently have a logged in user
- Expected Function to be called with { url : 'api/loggedin', type : 'GET', error : <jasmine.any(function Function() { [native code] })>, success : <jasmine.any(function Function() { [native code] })> }.
at Spec.<anonymous> (/Users/ritmatter/reps/spec/authTest.js:13:20)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
The code being tested is in a file called auth.jsx, and it looks like this:
loggedIn: function() {
return $.ajax({
url: 'api/loggedin',
type: 'GET',
error: function(xhr, status, err) {
return false;
}.bind(this),
success: function(data) {
return true;
}.bind(this),
});
},
The test looks like this:
/** #jsx React.DOM */
"use strict";
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
describe('Authentication: Logging In', function() {
it('Doesn\'t currently have a logged in user', function () {
var $ = require('jquery');
jest.dontMock('../js/auth.jsx');
var auth = require('../js/auth.jsx');
auth.loggedIn();
expect($.ajax).toBeCalledWith({
url: 'api/loggedin',
type: 'GET',
error: jasmine.any(Function),
success: jasmine.any(Function)
});
});
});
Any idea why jest would think that this is not getting called? I've been looking around, and it seems like there are some bugs with respect to dontMock() and mock().

As Wagner mentioned, you need to require jquery globally, outside of your test. Your component is using the global version of $, so adding var $ = require('jquery') doesn't do anything in terms of adding jquery to the global variable $.
You also did not mock the ajax call.
When testing react, I avoid issues with loading jquery by simply redefining $:
window.$ = {ajax: jest.genMockFunction()}
So, as long as you don't need jquery for anything else other than an ajax call, this one line will simulate the jquery root and mock the ajax call.

I had a similar problem with a React component that invoked ajax on initialisation.
What I found is that expect on $ only work if you require jquery outside the it method.
My React component and test case are like these (They are ES6 but you can get the idea)
import React from 'react'
import $ from 'jquery'
export default class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {things:[]};
}
componentWillMount() {
$.ajax({
url: 'http://localhost:3000/things',
success: (result) => this.setState(result),
error: (ex) => console.log(ex)
})
}
render() {
//stuff
}
}
and the test
jest.dontMock('../components/MyComponent')
import React from 'react'
import TestUtils from 'react-addons-test-utils'
import $ from 'jquery'
const Wall = require('../components/MyComponent');
describe('MyComponent', () => {
it('calls the things end point', () => {
const myComp = TestUtils.renderIntoDocument(<MyComponent />)
expect($.ajax).toBeCalledWith({
url: 'http://localhost:3000/things',
success: jasmine.any(Function),
error: jasmine.any(Function)
})
});
});

Related

ES6 call to static method throwing an error

I've got the following class in Javascript:
cart.js
class Cart{
static addItem(data, beforeSendCallback=null, successCallback=null, errorCallback=null) {
const emptyCallback = ()=>{}
if(data){
$.ajax({
url: '/orders/',
type: 'POST',
data: data,
beforeSend: beforeSendCallback == null ? emptyCallback : beforeSendCallback,
success: successCallback == null ? emptyCallback : successCallback,
error: errorCallback == null ? emptyCallback : errorCallback,
dataType: 'json'
})
}
}
}
Now, in a different file I've got:
item.js
...
function addItemToCart(e) {
e.preventDefault()
let data = {
'itemId': $('#item_id').val(),
'type': $('#item_type').val(),
'quantity': $('#quantity').val(),
'stock': $('#in_stock').val(),
'price': $('#item_price').val()
}
if (data.stock != 2) {
Cart.addItem(data, disableAddToCartBtn(true), disableAddToCartBtn(false))
}
}
...
When add-cart-btn is pressed then this function is executed but I'm getting an error:
Uncaught ReferenceError: Cart is not defined,
at HTMLAnchorElement.addItemToCart (item.js:20)
at HTMLAnchorElement.dispatch (jquery.js:5206)
at HTMLAnchorElement.elemData.handle (jquery.js:5014)
These two files are being added to app.js which is in charge of processing them via webpack:
require('./core/cart')
require('./item')
Since I'm requiring ./core/cart first I assumed its content would be available for ./item, am I wrong?
Try assigning the imported cart to a variable:
const Cart = require('./core/cart');
and make sure you export the cart in cart.js:
module.exports = class Cart {
You need to export the Cart class in cart.js, then import and actually assign it to a variable where you want to use it. In cart.js:
// ....
module.exports = Cart;
Then in whatever module you want to use Cart:
const Cart = require('./core/cart');
// use the `Cart` class.

React JS, load json data then manipulate it

Started to play around with react, just learning the small syntax stuff at the moment, im stuck on AJAX requests with data displaying.
The console gives me an error saying the local version of test.json cannot be found. It is in the same directory as the header.js file
//header.js
import React from 'react';
import $ from 'jquery';
var theData = [];
var Header = React.createClass({
getInitialState: function() {
return {
data: null
};
},
componentDidMount: function() {
$.ajax({
dataType: 'json',
url: "./test.json",
success: function(data) {
theData.push(data);
console.log(theData);
}
});
},
render: function() {
return (
<div>
<div id="theData" className="theData"></div>
{theData.someValue}
</div>
</div>
);
}
});
test.json might be in the same directory as header.js, but your code is running client-side and the client (browser) has no idea of what test.json is.
Instead, you should define an endpoint in your server-side logic to read the contents of test.json and return it back to the client as a JSON object. In your client-side logic, the URL property in your current XHR should be replaced with the URI to the endpoint.
Side note: your component as-is won't display any data after the XHR is complete. theData will be properly mutated but it won't trigger a component rerender. You should instead associate your XHR response JSON with component state (which you initialized properly in getInitialState), and React will rerender accordingly when its value is modified.
Update with a code example, assuming your server is Express.
On the server:
const fs = require('fs');
const app = ...
app.get('/name-this-endpoint-appropriately', (req, res) => {
const json = fs.readFileSync('test.json', 'utf8');
res.send(json);
});
On the client (with fixes as mentioned in the side note above):
import React from 'react';
import $ from 'jquery';
var Header = React.createClass({
getInitialState: function() {
return {
data: []
};
},
componentDidMount: function() {
$.ajax({
dataType: 'json',
url: "/name-this-endpoint-appropriately",
success: (data) => this.setState({data})
});
},
render: function() {
return (
<div>
<div id="theData" className="theData"></div>
{this.state.data.someValue}
</div>
</div>
);
}
});

React throwing error: Cannot read property 'map' of undefined

I am learning React. I am following a chapter in book where they load the data from API under componentDidMount and update the state of the component as
getInitialState: function () {
return {changeSets: []};
},
mapOpenLibraryDataToChangeSet : function (data) {
return data.map(function (change, index) {
return {
"when":jQuery.timeago(change.timestamp),
"who": change.author.key,
"description": change.comment
}
});
},
componentDidMount: function () {
$.ajax({
url: 'http://openlibrary.org/recentchanges.json?limit=10',
context: this,
dataType: 'json',
type: 'GET'
}).done(function (data) {
// console.log(data);
var changeSets = this.mapOpenLibraryDataToChangeSet(data);
console.log("changeSets:" + JSON.stringify(changeSets));
this.setState({changeSets: changeSets});
});
},
When I run this, I see error on console as
"TypeError: Cannot read property 'map' of undefined
at t.render (mikobuf.js:55:41)
at _renderValidatedComponentWithoutOwnerOrContext (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:17508)
at _renderValidatedComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:17644)
at performInitialMount (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:13421)
at mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:12467)
at Object.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:15:2892)
at h.mountChildren (https://npmcdn.com/react#15.3.1/dist/react.min.js:14:26368)
at h._createInitialChildren (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:26619)
at h.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:13:24771)
at Object.mountComponent (https://npmcdn.com/react#15.3.1/dist/react.min.js:15:2892)"
The running link is http://jsbin.com/mikobuf/edit?js,console,output
What am I doing wrong?
UPDATE
When I added the changeSets={data} while rendering the app, I see data in console
ReactDOM.render(<App headings = {headings} changeSets={data}/>, document.getElementById("container"))
But I want the data to be pulled from API. So as soon as I remove the changeSets={data} when rendering, it fails
ReactDOM.render(<App headings = {headings}/>, document.getElementById("container"))
You are trying to use the props changeSets when it is actually part of Apps state.
This:
<RecentChangesTable.Rows changeSets={this.props.changeSets} />
Should Be:
<RecentChangesTable.Rows changeSets={this.state.changeSets} />
http://jsbin.com/tuqeciyere/1/edit?js,console,output

Ember Simple Auth transition after login

I have login code on my application route, as per examples in the docs, but the call to authenticate does not seem to return a promise. The response I get in 'then' is undefined. Therefore the transition does not work. I have to manually refresh the page, and then the top redirect is called.
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(response); // undefined
this.transitionTo('orders'); // can't call on undefined
}
);
},
}
});
My issue was 'this' inside the function call was the wrong object. Solved by using var _this = this;
I'll post the full working code.;
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
var _this = this;
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(_this.get('session')); // this correctly gets the session
_this.transitionTo('orders');
}
);
},
}
});
The promise returned by the session's authenticate method doesn't resolve with a value. You can access data that the authenticator resolves with via the session's secure property, e.g. this.get('session.secure.token)'.

Constants not available for jest tests in Actions

When I run navAction-test.js below I get following error: TypeError: Cannot read property 'DATA_LOADED' of undefined
As I understand it the constant.DATA_LOADED is undefined, I'm guessing jest is mocking it and for this reason constant is undefined, I've tried everything I could find on the next but it remains undefined.
Is there anyone out there who can help. I would really appreciate it.
navAction.js:
var dispatcher = require('../dispatcher/AppDispatcher');
var constants = require('../constants/constants');
module.exports = {
load: function() {
def = $.ajax({
url: 'http://api.facebook.com/',
data: {},
success: function (data, textStatus, jqXHR) {
dispatcher.dispatch({type: constants.DATA_LOADED, data: data});
}
});
}
}
navAction-test.js:
jest.dontMock('../navAction.js');
describe('Tests NavigationCollectionActionCreators', function() {
var $;
var dispatcher;
var navAction;
beforeEach(function() {
$ = require('jquery');
dispatcher = require('../../dispatcher/AppDispatcher');
navAction = require('../navAction.js');
});
it('tests calls $.ajax & dispatcher ', function () {
navAction.load();
$.ajax.mock.calls[0][0].success({body: {header: {items: [{name: 'The name', link: 'http://www.facebook.com'}]}}});
expect(dispatcher.dispatch).toBeCalledWith({type: 'DATA_LOADED', data: [{name: 'The name', link: 'http://www.timeout.com'}]});
});
});
//constants.js
var keyMirror = require('keymirror');
module.exports = keyMirror({
DATA_LOADED: null,
});
UPDATE:
I got this to work by adding the following to navAction-test.js:
var constants = {
DATA_LOADED: 'DATA_LOADED',
DATA_NOT_LOADED: 'DATA_NOT_LOADED'
};
jest.setMock('../../constants/constants', constants);
Is this the correct way of doing it?
Add to test:
jest.dontMock('../constants/constants');
If you do not want to use automocking at all, you can remove all of the jest.dontMock and put instead:
jest.autoMockOff();
Your method to solve the problem (in the update at the bottom) is not appropriate in my opinion because it violates DRY (do not repeat yourself). What happens when you added another action constant? Now you have to remember to add it in the test too. That is problematic.

Categories

Resources