I am trying to write a unit test for a AngularJS service with Karma and Jasmine using the PhantomJS browser. The service that I want to test uses nested promises (for reading a IndexedDB database, getting responses from the server and writing the response back to the database).
The same has to be done when writing to the database (write local db, post/put to server, update local db with server id). The tests I am writing all work except for the tests for handling a loss of connection to the server.
When that happens the sync() method sets the online status to false and creates a $timeout that repeatedly checks for a connection by doing a recursive call. If the connection is up again, all unsynced data will be synced (and the promises of the corresponding sync() calls will be resolved).
describe('Harmonized', function() {
var ... // setting all 'global' variables
[...]
beforeEach(function(){
// Address that the service sends a ping to check if online again
pingGet = backend.when('GET', 'http://localhost:2403');
pingGet.respond(0);
testTablePut121 = backend.when('PUT', 'http://localhost:2403/test_table/121');
testTablePut121.respond(function(method, url, data, headers) { ... } // returns 200
[...]
}
[...]
it('should try to sync a SAVED element but should fail because of missing server connectivity', function() {
var list;
var timeoutCounter = 0;
_checkOnline = harmonized._checkOnline; // harmonized is the service that has to be testet
harmonized._checkOnline = function () {
// only call the original function two times
if (timeoutCounter++ < 2) {
_checkOnline();
// flush the $timeout and the $httpBackend
timeout.flush(2000);
backend.flush(1);
// this also gives me an error because of the $digest already in progress
console.log('after tick');
}
};
all.getList().then(function(entries) {
list = entries;
testTablePut121.respond(0);
pingGet = backend.expect('GET', 'http://localhost:2403');
pingGet.respond(0);
spyOn(list[0], 'sync').and.callThrough();
list[0].save().then(
// resolve is called when the server call was successfull
function() {
expect(list[0]._synchronized).toBeTruthy();
expect(list[0].sync.calls.count()).toBe(2);
console.log('done');
},
// reject is called when the server response an error other than 0 (which is not the case in this test spec)
null,
// notify is called when the local db save is made
function(){
expect(list[0].sync.calls.count()).toBe(1);
expect(list[0]._synchronized).toBeFalsy();
});
backend.flush(1);
});
backend.flush(1);
});
When the backend.flush() function is called a second time (to flush the http request made in the save() function, I get an error telling me that the $digest is already in progress. Omitting the line, doesn't flush the request (and a also get an error because of that).
Is there a way to flush everything at the right time without getting the $digest error?