How to Mock private functions that returns a promise karma unit test - karma-runner

I want to test a function(publicFunction) inside a factory(TestFactory). This functions calls a private function(privateFunction) that returns a promise with calling andother function(Caller) inside another factory(callerfactory). How do I mock that private function?
module.factory('TestFactory',['callerfactory','$q',function($q,callerfactory){
function privateFunction(varr){
var deferred = $q.defer( );
var data = new Caller(varr);
varr.init().then( function( ) {
deferred.resolve( vari ); //This is important to be resolved
} );
return deferred.promise;
}
this.publicFunction = function(list){
var promises = [];
angular.forEach(list.find('ind'), function (sampleVar,index) {
promises.push( privateFunction(sampleVar));
}
$q.all(promises).then ( function (status) ) {
Varriedlist = status; //I want to test if this list whould be filled
deferred.resolve( true );
}
return deferred.promise;
}
} ] );
And the Caller factoy:
module.factory( 'callerfactory', function() {
var caller = function (varToCall) {
var _varToCall= this;
this.init = function () {
var deferred = $q.defer( );
//some implementations
return deferred.promise;
}
}
return caller;
}
I am using karma for unit test and jasmine 2 framework.

Related

If use async/await, it transpiles code above my "imports" and makes my func undefined

I currently have this code:
// Imports
const {utils: Cu, Constructor: CC} = Components;
Cu.import('resource://gre/modules/Services.jsm');
Services.scriptloader.loadSubScript('chrome://trigger/content/webextension/scripts/3rd/polyfill.min.js');
function install() {}
function uninstall() {}
function shutdown() {}
function startup() { // this gets automatically called by the environment i am designing for
Services.prompt.alert(Services.wm.getMostRecentWindow('navigator:browser'), 'title', 'body');
}
async function doAsync() {
return await new Promise(() => setTimeout(()=>resolve('done'), 5000));
}
However after compiling with babel with this .babelrc:
{
"presets": ["es2015", "es2017"],
"plugins": ["transform-object-rest-spread"],
"ignore": [
"3rd/**/*",
]
}
It moves my doAsync function to the top. Before the imports. Which is a problem. Because the import needs to happen right away. As startup gets called right away.
The compiled code becomes this:
'use strict';
var doAsync = function () {
var _ref = _asyncToGenerator(regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return new Promise(function () {
return setTimeout(function () {
return resolve('done');
}, 5000);
});
case 2:
return _context.abrupt('return', _context.sent);
case 3:
case 'end':
return _context.stop();
}
}
}, _callee, this);
}));
return function doAsync() {
return _ref.apply(this, arguments);
};
}();
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
// Imports
var _Components = Components,
Cu = _Components.utils,
CC = _Components.Constructor;
Cu.import('resource://gre/modules/Services.jsm');
Services.scriptloader.loadSubScript('chrome://trigger/content/webextension/scripts/3rd/polyfill.min.js');
function install() {}
function uninstall() {}
function shutdown() {}
function startup() {
// this gets automatically called by the environment i am designing for Services.prompt.alert(Services.wm.getMostRecentWindow('navigator:browser'), 'title', 'body');
}
Is there anyway so that if I use async/await that it doesn't move stuff to above my imports? If my imports at the top of the file, it fails to run. Because the imports brings in the polyfill.min.js

ionic service init before loading controller

I have service which has asynchronous init of parameter and I want that every controller to wait until the init will be finished.
The problem is that the getObject method is being called by the controller before the init of parameters variable is finished.
The service:
angular.module('core')
.factory('localstorage', ['$window', '$cordovaSQLite', '$q',
function ($window, $cordovaSQLite, $q) {
var db;
var parameters = {};
if (window.cordova) {
db = $cordovaSQLite.openDB({name: "my.db"}); //device
} else {
db = window.openDatabase("my.db", '1', 'life', 1024 * 1024 * 100);
}
var promise = $q(function (resolve, reject) {
$cordovaSQLite.execute(db, "SELECT * FROM life_table")
.then(function (res) {
if (res.rows.length == 0) {
$cordovaSQLite.execute(db,
"CREATE TABLE IF NOT EXISTS life_table (key text primary key, value text)");
}
else {
for (var i = 0; i < res.rows.length; ++i) {
parameters[res.rows[i].key] = JSON.parse(res.rows[i].value);
}
}
resolve(parameters);
});
});
return {
promise: promise,
getObject: function (key) {
var query = "SELECT value FROM life_table where key = ?";
$cordovaSQLite.execute(db, query, [key]).then(function (res) {
if (res.rows.length > 0) {
console.log("getObject: " + res.rows[0].value);
}
}, function (err) {
console.error(err);
});
return parameters[key];
}
}
}]);
the config:
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('navigator', {
url: '/',
abstract: true,
controller: 'NavigatorCtrl',
resolve: {
'MyServiceData': function (localstorage) {
// localstorage will also be injectable in the controller
return localstorage.promise;
}
}
})
.state('login', {
url: '/login',
templateUrl: 'templates/login.html',
controller: 'LoginCtrl',
controllerAs: 'loginCtrl',
resolve: {
'MyServiceData': function (localstorage) {
// localstorage will also be injectable in the controller
return localstorage.promise;
}
}
})
Shalom Dor!
Option 1
Make a chain of promises and return the factory only after all promises are resolved. This will prevent the controller from running as the factory dependency will not be returned until the desired promises are resolved.
Option 2
Create some sort initialization function in your service that returns a promise (you can use $q). Execute it inside $ionicPlaform.ready() and only run the controller logic after you got the promise.
In the controller:
MyService.initialize().then(function () {
// Service initialized, do stuff...
});
In the service:
returned.initialize = function () {
var d = q.defer();
// Do stuff...
// Resolve 'd' when you finished initializing the service using 'd.resolve()'.
return d.promise;
};

Protractor specs leaking

I'm still quite new to promises and the like and I need some help with this problem. One of my it blocks does not end before the next one begins ending up in a StaleElementReferenceError a whole specfile later from where the code was supposed to be called.
listView.js (I know it looks weird but I set it up this way for an unrelated reason):
module.exports = function () {
var public = {};
public.checkFilters = function (filters) {
var promises = [];
for (var i = 0; i < filters.length; i++) {
promises[i] = getFilterPromise(filters[i]);
}
return protractor.promise.all(promises);
};
var getFilterPromise = function (filter) {
return public.getHeaderIndex(filter.on).then(function (headerIndex) {
return checkRows(filter.values, headerIndex);
});
};
public.getHeaderIndex = function (text) {
var headers = table.all(by.tagName('th'));
var correctHeaderIndex;
return headers.each(function (header, index) {
header.getText().then(function (actualHeaderText) {
if (actualHeaderText === text) {
correctHeaderIndex = index;
}
})
}).then(function () {
return new Promise(function (resolve, reject) {
if (correctHeaderIndex) {
resolve(correctHeaderIndex);
} else {
reject('Header not found');
}
});
});
};
public.getWorkflowCount = function () {
return workflows.count();
};
var checkRows = function (matchers, headerIndex) {
var mismatch = false;
return workflows.each(function (element, index) {
public.getTextFromCell(index, headerIndex).then(function (actual) {
if (!anyMatch(actual, matchers)) {
mismatch = true;
}
});
}).then(function () {
return new Promise(function (resolve, reject) {
if (mismatch) {
reject('Header not found');
} else {
resolve('all rows matched');
}
});
});
};
var anyMatch = function (actual, matchers) {
var match = false;
for (var j = 0; j < values.length; j++) {
if (text === values[j]) {
match = true;
}
}
return match;
};
public.getTextFromCell = function (row, column) {
return workflows.get(row).all(by.tagName('td')).get(column).getText();
};
return public;
}();
LV_00:
describe('LV_00:', function () {
it('statusfilter', function () {
P.listView.filter('status', H.regStatus.S.inProgress);
});
it('statusfilter works', function () {
P.listView.checkFilters([{
on: H.lang.S.status,
values: [H.regStatus.S.inProgress]
}]);
});
});
I think you should move the test preparation code into the beforeEach():
describe('LV_00:', function () {
beforeEach('statusfilter', function () {
P.listView.filter('status', H.regStatus.S.inProgress);
});
it('statusfilter works', function () {
P.listView.checkFilters([{
on: H.lang.S.status,
values: [H.regStatus.S.inProgress]
}]);
});
});
You may also need to use the done callback function:
describe('LV_00:', function (done) {
beforeEach('statusfilter', function () {
P.listView.filter('status', H.regStatus.S.inProgress).then(function () {
done();
});
});
it('statusfilter works', function () {
P.listView.checkFilters([{
on: H.lang.S.status,
values: [H.regStatus.S.inProgress]
}]);
});
});
assuming filter() returns a promise.
Found the solution thanks to alecxe proposing to use done() I used the following after some googling around.
it('statusfilter', function () {
P.listView.filter('status', H.regStatus.S.inProgress);
});
it('statusfilter works', function () {
protractor.promise.controlFlow().execute(function () {
return P.listView.checkFilters([{
on: H.lang.S.status,
values: [H.regStatus.S.inProgress]
}]);
});
});
Found here: Prevent Protractor from finishing before promise has been resolved

No Data from Service to Controller to Scope -> Result Undefined Angularjs Ionic

My problem is, that the controller just send an undefiend and not the data from http of service. I inspect it with chrome. I am new at ionic. By calling the AppSqliDBFactory.getMasterdataId() method, it shows an undefiend, also at the scope variable.
.controller('ReadMasterdataCtrl', function ($scope, $state, $ionicNavBarDelegate, MasterdataService, AppSqliDBFactory){
$scope.masterdataId;
$scope.masterdataData;
AppSqliDBFactory.getMasterdataId().then( function (masterdata){
$scope.masterdataId = masterdata[0].masterdataId;
}).catch(function (err){
console.log(err);
});
//here is the error -> no data at "$scope.masterdataData = masterdata;"
MasterdataService.getMasterdataDB($scope.masterdataId)
.then(function (masterdata) {
$scope.masterdataData = masterdata;
console.log("getMasterdataDB respont");
console.log($scope.masterdataData);
}).catch(function (err) {
console.log(err);
});
})
//Service
.factory('MasterdataService', function ($q, $http, SERVER_URL) {
//Create JSON Object
var srv = {};
//Array for JSON Objects
srv.masterdata = [];
srv.getMasterdataDB = function (masterdataId) {
var deferred = $q.defer();
var masterdata;
var masterdataId = masterdataId;
var baseUrl = 'xxxx';
$http.get(SERVER_URL + baseUrl + masterdataId).success(function (response){
masterdata = response[0];
console.log(masterdata);
return deferred.resolve(masterdata);
}).error(function (err){
return deferred.reject(err);
});
return deferred.promise;
//return srv.getMasterdata();
};
// Public API
return {
getMasterdataDB: function ( masterdataId) {
return $q.when(srv.getMasterdataDB( masterdataId));
}
};
});
Simplified:
AppSqliDBFactory.getMasterdataId().then(function (masterdata) {
$scope.masterdataId = masterdata[0].masterdataId;
});
MasterdataService.getMasterdataDB($scope.masterdataId).then(function (masterdata) {
$scope.masterdataData = masterdata;
});
When MasterdataService.getMasterdataDB() is called, AppSqliDBFactory.getMasterdataId() may not have been resolved yet, so $scope.masterdataId can be undefined (which is probably what is happening in your case).
You have to call AppSqliDBFactory.getMasterdataId() after AppSqliDBFactory.getMasterdataId() has been resolved:
AppSqliDBFactory.getMasterdataId().then(function (masterdata) {
$scope.masterdataId = masterdata[0].masterdataId;
MasterdataService.getMasterdataDB($scope.masterdataId).then(function (masterdata) {
$scope.masterdataData = masterdata;
});
});
Or with chaining:
AppSqliDBFactory.getMasterdataId().then(function (masterdata) {
$scope.masterdataId = masterdata[0].masterdataId;
return MasterdataService.getMasterdataDB($scope.masterdataId);
}).then(function (masterdata) {
$scope.masterdataData = masterdata;
});

KnockoutJS : initial values are not posted to server when using ko.toJSON(this)?

I've this javascript viewmodel defined:
function PersonViewModel() {
// Data members
this.Name = ko.observable();
this.Function_Id = ko.observable();
this.SubFunction_Id = ko.observable();
this.Functions = ko.observableArray();
this.SubFunctions = ko.observableArray();
// Whenever the Function changes, update the SubFunctions selection
this.Function_Id.subscribe(function (id) {
this.GetSubFunctions(id);
}, this);
// Functions to get data from server
this.Init = function () {
this.GetFunctions();
this.Function_Id('#(Model.Function_Id)');
};
this.GetFunctions = function () {
var vm = this;
$.getJSON(
'#Url.Action("GetFunctions", "Function")',
function (data) {
vm.Functions(data);
}
);
};
this.GetSubFunctions = function (Function_Id) {
var vm = this;
if (Function_Id != null) {
$.getJSON(
'#Url.Action("GetSubFunctions", "Function")',
{ Function_Id: Function_Id },
function (data) {
vm.SubFunctions(data);
}
);
}
else {
vm.SubFunction_Id(0);
vm.SubFunctions([]);
}
};
this.Save = function () {
var PostData = ko.toJSON(this);
var d = $.dump(PostData);
alert(d);
$.ajax({
type: 'POST',
url: '/Person/Save',
data: PostData,
contentType: 'application/json',
success: function (data) {
alert(data);
}
});
};
}
$(document).ready(function () {
var personViewModel = new PersonViewModel();
personViewModel.Init();
ko.applyBindings(personViewModel);
});
When the Submit button is clicked, the data from the select lists is posted, but NOT the 'Function_Id'.
When I choose a different value in the Function dropdown list, and the click the Submit button, the value for 'Function_Id' is correctly posted.
How to fix this ?
It's because the scope of the this keyword in javascript
this.Init = function () {
this.GetFunctions(); // this === PersonViewModel.Init
this.Function_Id('#(Model.Function_Id)'); // calls PersonViewModel.Init.Function_Id(...)
};
You should store the refrence to the PersonViewModel instance.
var self = this;
self.Init = function () {
self.GetFunctions();
self.Function_Id('#(Model.Function_Id)'); // calls PersonViewModel.Function_Id(...)
};