Mocking asynchronous requests within react's getInitialState - javascript

When i created this question, my doubt was about how would i be able to test an asynchronous request utilizing mocha/enzyme/chai/sinon.
I am sure that there are different ways, but a possible one is to mock it with a handmade function that returns the appropriate values (check the answer for details).
My getInitialState method is this:
getInitialState: function() {
var me = this;
var documentData = null;
var promise = me.getDocuments();
promise.then(function(value) {
var documents = value.map(function(obj) {
return Object.keys(obj).sort().map(function(key) {
return obj[key];
});
});
documentData = documents;
});
return ({
cd: false
});
},
And the getDocuments() function that returns a promise is this:
getDocuments: function() {
var deferred = when.defer();
Collection.fetch({cd: workspaceInfo.getCD()}).then(
function(results) {
deferred.resolve(results);
},
deferred.reject
);
return deferred.promise;
},
How can i successfuly test it?
Would i have to mock the getInitialState method itself? (is that even possible)
Or just the getDocuments function with some predictable return values?
Thanks in advance, any help will be appreciated.

I solved this by requiring the Collection (which is a rest API that brings some values from a database)
var Collection = require("path/to/my/collection/Collection");
Afterwards, i use it in my getDefaultProps() method:
getDefaultProps() {
return {
Collection: new Collection()
};
},
And this in turn enables me to write tests that initialize a mocked Collection (fileDataResponse is a JSON with some data):
var CollectionMock= {
fetch: () => {
return {
then: callback => callback(fileDataResponse)
};
}
};
And use it in my test afterwards:
it("should open the modal without a loaded configuration", function() {
var instance, wrapper;
wrapper = mount(
<DocumentPreview
Collection={CollectionMock}/>
);
instance = wrapper.component.getInstance();
instance.openModal();
expect(wrapper.find('#MockedTest' + 'docOid251085').exists()).to.equal(true);
wrapper.unmount();
});

Related

Testing a method of an object returned by a function

I have a function that returns an object with many methods and I need to check one of the methods inside this returned object. I am using AngularJS and Karma+Jasmine as testing suite. How do I call methods inside the object returned by a function?
function modalOptions() {
.........
return this.$q((resolve) => {
// test accessable here
this.solveModel = {
save: () => {
// test can't call save()
this.saveToDB = this.toSendToDB;
},
cancel: () => { ...
},
delete: () => { ...
}
};
});
}
My test is somewhat like this...
it('should save modal with the data', function() {
scope.$apply();
expect(vm.modalOptions).toBeDefined();
vm.toSendToDB = true; // hard-coded
vm.savedToDB = undefined // default value from other controller
spyOn(vm, 'modalOptions').and.callThrough();
console.log(vm.modalOptions()); // gives weird response: c{$$state: Object{status: 0}} instead of the solveModal object
expect(vm.toSendToDB).toBeTruthy();
expect(vm.savedToDB).toBeTruthy();
});
Sorry, I can not comment yet, but the promise has to be resolved and the solveModel passed to it, in order for solveModel to be returned. Where do you resolve the promise?

How to get Ember component object inside promise success or error call back?

I have pass two callback function success and error on promise returned from ajax call using then method. Now i am unable to get Ember component object inside success/error method.
import Ember from 'ember';
export default Ember.Component.extend({
data:null,
issueType:'',
description:null,
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
this.set('ticket.category',data.category);
this.set('ticket.name',this.get('session.currentUser.first_name'));
this.set('ticket.phone',this.get('session.currentUser.phone'));
this.set('ticket.groupId',data.groupId);
this.set('ticket.ownerId',this.get('session.currentUser.id'));
this.set('ticket.oyoId',this.get('session.currentOwnerHotelOyoId'));
this.set('ticket.ticketRaisedBy','owner');
this.set('ticket.bookingId',data.bookingId);
this.set('ticket.subType',data.subType);
this.set('ticket.subSubIssue',data.subSubIssue);
this.set('ticket.email',this.get('ticket.oyoId')+'#oyoproperties.com');
this.set('ticket.subject',this.get('ticket.oyoId')+' : '+this.get('ticket.category'));
this.set('ticket.description',this.get('description'));
},
success:function(){
console.log(this.get('description'));
},
error:function(){
console.log(this.get('description'));
},
actions :{
submitIssue:function(){
this.prepareSubmitRaiseIssueModal();
this.get('ticket').submitRaiseIssue().then(this.success,this.error);
//this.send('closeRaiseIssueModal');
},
closeRaiseIssueModal:function(){
this.sendAction('closeRaiseIssueModal');
}
}
});
i am able to get Ember component object if instead of passing named function i pass anonymous function.
submitIssue:function(){
var self = this;
this.prepareSubmitRaiseIssueModal();
this.get('ticket').submitRaiseIssue().then(function(response){
console.log(self.get('description'));
},
function(err){
console.log(self.get('description'));
});
//this.send('closeRaiseIssueModal');
},
is there any way i can get the Ember component object's reference for former case??
Wow speaking of a spaghetti.
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
this.set('ticket.category',data.category);
this.set('ticket.name',this.get('session.currentUser.first_name'));
this.set('ticket.phone',this.get('session.currentUser.phone'));
this.set('ticket.groupId',data.groupId);
this.set('ticket.ownerId',this.get('session.currentUser.id'));
this.set('ticket.oyoId',this.get('session.currentOwnerHotelOyoId'));
this.set('ticket.ticketRaisedBy','owner');
this.set('ticket.bookingId',data.bookingId);
this.set('ticket.subType',data.subType);
this.set('ticket.subSubIssue',data.subSubIssue);
this.set('ticket.email',this.get('ticket.oyoId')+'#oyoproperties.com');
this.set('ticket.subject',this.get('ticket.oyoId')+' : '+this.get('ticket.category'));
this.set('ticket.description',this.get('description'));
},
How about
prepareSubmitRaiseIssueModal:function(){
var data = this.get('data');
var ticket = this.get('ticket')
ticket.setProperties({
'category': data.category,
'name': ...
})
},
And to pass reference's you can either use
promise.then(function() {
this.mysuccess();
}.bind(this), function() {
this.myerror();
}.bind(this))
const self = this;
promise.then(function() {
self.blah();
});
promise.then(result => { 
this.blah();
}) 
In your case I would write a utility JS file for displaying notifications.
And handle success for each promise personally and let errors be handled in a general error method.
utils/notifications.js
function reportError(error) {
displayNotification('error', getErrorMessage(error));
}
import reportError from 'utils/notifications';
export default Ember.Controller.extend({
....
promise.then(result => {
// Do custom stuff with result;
}, reportError);
....
});
And promise within a promise
return promise1.then(x => {
return promise2.then(x2 => {
return promise3 ... etc
})
}).catch(reportError); // Single hook needed but u need to return promises

Jasmine, Parse.com&Promises: Testing functions that call query.get/find

I'm trying to get my head around testing a parse.com backed data service, but have not found a satisfying solution so far. Basically, I want to use karma/jasmine to test a function returning a promise.
I'm spying on the query.get() method to intercept and return an object that was created in the implementation of the jasmine spec. However, when the spy returns the local object, the success path of the query.get options argument is not executed. Instead, the to-be-tested function directly returns the unresolved promise, which is not what I want. Basically, I want to test the success path (and the error path) of the data service.
Here's the code:
to-be-tested parse.com data services
angular.module('app.services',[])
.factory('AppServices',function(){
var doFunction1 = function(){
var promise = new Parse.Promise();
var ParseObjectClass = Parse.Object.extend('ParseObject');
var query = new Parse.Query(ParseObjectClass);
query.get('abc1234',{
success: function(result){
promise.resolve(result);
},
error: function(result, error){
promise.reject(error);
}
});
return promise;
}
return {
function1: doFunction1
}
});
jasmine spec
describe('MyServices Tests', function() {
var AppServices;
var scope;
beforeEach(function() {
module('app.services');
});
beforeEach(inject(function($rootScope, AppServices) {
AppServices = AppServices;
scope = $rootScope.$new();
Parse.initialize("key1", "key2");
var user = new Parse.User({
id: 'abc1234',
});
var ParseObjectClass = Parse.Object.extend('ParseObject');
var obj1 = new ParseObjectClass({
id: 'xyc789'
});
spyOn(Parse.Query.prototype, 'get')
.and.callFake(function(options) {
return Parse.Promise.as(obj1);
});
}));
it('parseMockTest', function() {
var result = AppServices.function1(2);
console.log(JSON.stringify(result));
});
});
This is the result from the karma log:
LOG: '{"_resolved":false,"_rejected":false,"_resolvedCallbacks":[],"_rejectedCallbacks":[]}'
I would have expected that the returned promise is resolved.
Any ideas/hints on how to test functions that return promises?

angular controller is executing before factory complete

I have moved some common code to factory. but the controller is executing before factory get loaded. In this case i am getting the blank response(zero results)
can anyone suggest the best solution.
here is my angular factory,
app.factory('TabsFactory', function($resource){
var activetabs = {};
activetabs.getDepositAccountDetails = function() {
return $resource('xxxx/:number', {}, {
getDepositAccountDetailsService: {
method: 'GET',
isArray: false
}
});
}
activetabs.getAccountInfo = function(){
return accountinit.accountInfo;
}
activetabs.setAccountInfo = function(accountnumber, result) {
var accountinit = {
accountInfo: []
}
if (result.code == "v") {
activetabs.getDepositAccountDetails().getDepositAccountDetailsService({
number: accountnumber
}).$promise.then(function(response) {
accountinit.accountInfo = response;
//here i am getting the JSON response
}, function(error) {
});
}
return accountinit;
}
return activetabs;
});
controller,
TabsFactory.setAccountInfo(accountnumber, $scope.accountInfo);
$scope.accountInfo = TabsFactory.getAccountInfo();
alert(JSON.stringify($scope.accountInfo));
You should use chain promise to update scope variable, because your accountInfo variable is updated inside $resource promise.
Code
TabsFactory.setAccountInfo(accountnumber, $scope.accountInfo).then(function(data){
$scope.accountInfo = TabsFactory.getAccountInfo();
alert(JSON.stringify($scope.accountInfo));
});
Update
Service method should return promise inorder to continue promise chain
activetabs.setAccountInfo = function(accountnumber, result) {
var accountinit = {
accountInfo: []
}
if (result.code == "v") {
//added return below
return activetabs.getDepositAccountDetails().getDepositAccountDetailsService({
number: accountnumber
}).$promise.then(function(response) {
accountinit.accountInfo = response;
return accountinit.accountInfo;
//here i am getting the JSON response
}, function(error) {
});
}
return accountinit;
}
Yes, this will happen because of JavaScript executing asynchronous operations but your controller in such a way that it expects things to be synchronous operations.
When you call TabsFactory.getAccountInfo() its possible that your $resource('xxxx/:number') is still not completed and response ready for you to process!!
So, what to do? You have make use of promise. I usually have a repository (A factory with method that return promise) to handle server communications. Here is an example:
app.factory('accountRepository', ["$http","$q",function($http,$q){
return {
getDepositAccountDetails : function(id) {
var deferred = $q.defer();
$http.ger('xxx').success(deferred.resolve).error(deferred.reject);
return deferred.promise;
}
};
}] );
My repository will have more operations like add account, update account info etc..
my controller/service then calls these methods as follows:
accountRepository.getDepositAccountDetails(123).then(function(response) {
// Process the response..
}, function(error) {
// Some error occured! handle it
});
doing so, my code gets executed only after I get response from server and data is ready for consumption or display. Hope this helps..
Update: You might want to have a look at this to get the idea ;)

collection.fetch() does not yield me response

exports.definition = {
config : {
// table schema and adapter information
},
extendModel: function(Model) {
_.extend(Model.prototype, {
// Extend, override or implement the Backbone.Model methods
});
return Model;
},
extendCollection: function(Collection) {
_.extend(Collection.prototype, {
}); // end extend
return Collection;
}
}
When i try to do var model = Alloy.createCollection('model');
alert(appointments.fetch());
I don't get any result.
fetch is asynchronous. As so, it will return a promise, or you need to set up a callback:
appointments.fetch().done(function( r ) { console.log(r) });
Also, never use alert to debug; this is useless. Always open your console and use console.log

Categories

Resources