Skip to content
This repository was archived by the owner on Mar 17, 2025. It is now read-only.

Commit 1197e1b

Browse files
author
Jacob Wenger
committed
Merge pull request #497 from jamestalmage/test-coverage
Add test coverage.
2 parents a8c1585 + cc149b9 commit 1197e1b

File tree

5 files changed

+173
-17
lines changed

5 files changed

+173
-17
lines changed

src/firebase.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -212,14 +212,6 @@
212212
}
213213
}
214214

215-
function assertArray(arr) {
216-
if( !angular.isArray(arr) ) {
217-
var type = Object.prototype.toString.call(arr);
218-
throw new Error('arrayFactory must return a valid array that passes ' +
219-
'angular.isArray and Array.isArray, but received "' + type + '"');
220-
}
221-
}
222-
223215
var def = $firebaseUtils.defer();
224216
var array = new ArrayFactory($inst, destroy, def.promise);
225217
var batch = $firebaseUtils.batch();
@@ -256,17 +248,27 @@
256248
}
257249
}
258250
});
251+
252+
assertArray(array);
253+
259254
var error = batch(array.$$error, array);
260255
var resolve = batch(_resolveFn);
261256

262257
var self = this;
263258
self.isDestroyed = false;
264259
self.getArray = function() { return array; };
265260

266-
assertArray(array);
267261
init();
268262
}
269263

264+
function assertArray(arr) {
265+
if( !angular.isArray(arr) ) {
266+
var type = Object.prototype.toString.call(arr);
267+
throw new Error('arrayFactory must return a valid array that passes ' +
268+
'angular.isArray and Array.isArray, but received "' + type + '"');
269+
}
270+
}
271+
270272
function SyncObject($inst, ObjectFactory) {
271273
function destroy(err) {
272274
self.isDestroyed = true;

tests/automatic_karma.conf.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ module.exports = function(config) {
1717
coverageReporter: {
1818
reporters: [
1919
{
20-
type: "lcovonly",
20+
// Nice HTML reports on developer machines, but not on Travis
21+
type: process.env.TRAVIS ? "lcovonly" : "lcov",
2122
dir: "coverage",
2223
subdir: "."
2324
},

tests/unit/FirebaseArray.spec.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,15 @@ describe('$FirebaseArray', function () {
390390
arr.$$notify('child_removed', 'removedkey123', 'prevkey456');
391391
expect(spy).not.toHaveBeenCalled();
392392
});
393+
394+
it('calling the deregistration function twice should be silently ignored', function(){
395+
var spy = jasmine.createSpy('$watch');
396+
var off = arr.$watch(spy);
397+
off();
398+
off();
399+
arr.$$notify('child_removed', 'removedkey123', 'prevkey456');
400+
expect(spy).not.toHaveBeenCalled();
401+
});
393402
});
394403

395404
describe('$destroy', function() {
@@ -398,6 +407,14 @@ describe('$FirebaseArray', function () {
398407
expect(arr.$$$destroyFn).toHaveBeenCalled();
399408
});
400409

410+
it('should only call destroyFn the first time it is called', function() {
411+
arr.$destroy();
412+
expect(arr.$$$destroyFn).toHaveBeenCalled();
413+
arr.$$$destroyFn.calls.reset();
414+
arr.$destroy();
415+
expect(arr.$$$destroyFn).not.toHaveBeenCalled();
416+
});
417+
401418
it('should empty the array', function() {
402419
expect(arr.length).toBeGreaterThan(0);
403420
arr.$destroy();
@@ -601,6 +618,16 @@ describe('$FirebaseArray', function () {
601618
expect(spy).toHaveBeenCalled();
602619
});
603620

621+
it('"child_added" should not invoke $$notify if it already exists after prevChild', function() {
622+
var spy = jasmine.createSpy('$$notify');
623+
var arr = stubArray(STUB_DATA, $FirebaseArray.$extendFactory({ $$notify: spy }));
624+
var index = arr.$indexFor('e');
625+
var prevChild = arr.$$getKey(arr[index -1]);
626+
spy.calls.reset();
627+
arr.$$process('child_added', arr.$getRecord('e'), prevChild);
628+
expect(spy).not.toHaveBeenCalled();
629+
});
630+
604631
///////////////// UPDATE
605632

606633
it('should invoke $$notify with "child_changed" event', function() {
@@ -647,6 +674,16 @@ describe('$FirebaseArray', function () {
647674
expect(spy).toHaveBeenCalled();
648675
});
649676

677+
it('"child_moved" should not trigger $$notify if prevChild is already the previous element' , function() {
678+
var spy = jasmine.createSpy('$$notify');
679+
var arr = stubArray(STUB_DATA, $FirebaseArray.$extendFactory({ $$notify: spy }));
680+
var index = arr.$indexFor('e');
681+
var prevChild = arr.$$getKey(arr[index - 1]);
682+
spy.calls.reset();
683+
arr.$$process('child_moved', arr.$getRecord('e'), prevChild);
684+
expect(spy).not.toHaveBeenCalled();
685+
});
686+
650687
///////////////// REMOVE
651688
it('should remove from local array', function() {
652689
var len = arr.length;
@@ -665,6 +702,23 @@ describe('$FirebaseArray', function () {
665702
arr.$$process('child_removed', arr.$getRecord('e'));
666703
expect(spy).toHaveBeenCalled();
667704
});
705+
706+
it('"child_removed" should not trigger $$notify if the record is not in the array' , function() {
707+
var spy = jasmine.createSpy('$$notify');
708+
var arr = stubArray(STUB_DATA, $FirebaseArray.$extendFactory({ $$notify: spy }));
709+
spy.calls.reset();
710+
arr.$$process('child_removed', {$id:'f'});
711+
expect(spy).not.toHaveBeenCalled();
712+
});
713+
714+
//////////////// OTHER
715+
it('should throw an error for an unknown event type',function(){
716+
var arr = stubArray(STUB_DATA);
717+
expect(function(){
718+
arr.$$process('unknown_event', arr.$getRecord('e'));
719+
}).toThrow();
720+
});
721+
668722
});
669723

670724
describe('$extendFactory', function() {

tests/unit/FirebaseObject.spec.js

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
describe('$FirebaseObject', function() {
22
'use strict';
3-
var $firebase, $FirebaseObject, $utils, $rootScope, $timeout, obj, $fb, testutils, $interval;
3+
var $firebase, $FirebaseObject, $utils, $rootScope, $timeout, obj, $fb, testutils, $interval, log;
44

55
var DEFAULT_ID = 'recc';
66
var FIXTURE_DATA = {
@@ -11,8 +11,18 @@ describe('$FirebaseObject', function() {
1111
};
1212

1313
beforeEach(function () {
14+
log = {
15+
error:[]
16+
};
17+
1418
module('firebase');
15-
module('testutils');
19+
module('testutils',function($provide){
20+
$provide.value('$log',{
21+
error:function(){
22+
log.error.push(Array.prototype.slice.call(arguments));
23+
}
24+
})
25+
});
1626
inject(function (_$firebase_, _$interval_, _$FirebaseObject_, _$timeout_, $firebaseUtils, _$rootScope_, _testutils_) {
1727
$firebase = _$firebase_;
1828
$FirebaseObject = _$FirebaseObject_;
@@ -252,6 +262,34 @@ describe('$FirebaseObject', function() {
252262
expect($scope.test).toEqual({foo: 'bar', $id: obj.$id, $priority: obj.$priority});
253263
});
254264

265+
it('will replace the object on scope if new server value is not deeply equal', function () {
266+
var $scope = $rootScope.$new();
267+
obj.$bindTo($scope, 'test');
268+
$timeout.flush();
269+
$fb.$set.calls.reset();
270+
obj.$$updated(fakeSnap({foo: 'bar'}));
271+
obj.$$notify();
272+
flushAll();
273+
var oldTest = $scope.test;
274+
obj.$$updated(fakeSnap({foo: 'baz'}));
275+
obj.$$notify();
276+
expect($scope.test === oldTest).toBe(false);
277+
});
278+
279+
it('will leave the scope value alone if new server value is deeply equal', function () {
280+
var $scope = $rootScope.$new();
281+
obj.$bindTo($scope, 'test');
282+
$timeout.flush();
283+
$fb.$set.calls.reset();
284+
obj.$$updated(fakeSnap({foo: 'bar'}));
285+
obj.$$notify();
286+
flushAll();
287+
var oldTest = $scope.test;
288+
obj.$$updated(fakeSnap({foo: 'bar'}));
289+
obj.$$notify();
290+
expect($scope.test === oldTest).toBe(true);
291+
});
292+
255293
it('should stop binding when off function is called', function () {
256294
var origData = $utils.scopeData(obj);
257295
var $scope = $rootScope.$new();
@@ -346,6 +384,31 @@ describe('$FirebaseObject', function() {
346384
});
347385
});
348386

387+
describe('$watch', function(){
388+
it('should return a deregistration function',function(){
389+
var spy = jasmine.createSpy('$watch');
390+
var off = obj.$watch(spy);
391+
obj.foo = 'watchtest';
392+
obj.$save();
393+
flushAll();
394+
expect(spy).toHaveBeenCalled();
395+
spy.calls.reset();
396+
off();
397+
expect(spy).not.toHaveBeenCalled();
398+
});
399+
400+
it('additional calls to the deregistration function should be silently ignored',function(){
401+
var spy = jasmine.createSpy('$watch');
402+
var off = obj.$watch(spy);
403+
off();
404+
off();
405+
obj.foo = 'watchtest';
406+
obj.$save();
407+
flushAll();
408+
expect(spy).not.toHaveBeenCalled();
409+
});
410+
});
411+
349412
describe('$remove', function() {
350413
it('should return a promise', function() {
351414
expect(obj.$remove()).toBeAPromise();
@@ -390,6 +453,13 @@ describe('$FirebaseObject', function() {
390453
expect(obj.$$$destroyFn).toHaveBeenCalled();
391454
});
392455

456+
it('should NOT invoke destroyFn if it is invoked a second time', function () {
457+
obj.$destroy();
458+
obj.$$$destroyFn.calls.reset();
459+
obj.$destroy();
460+
expect(obj.$$$destroyFn).not.toHaveBeenCalled();
461+
});
462+
393463
it('should dispose of any bound instance', function () {
394464
var $scope = $rootScope.$new();
395465
spyOnWatch($scope);
@@ -507,6 +577,20 @@ describe('$FirebaseObject', function() {
507577
});
508578
});
509579

580+
describe('$$error',function(){
581+
it('will log an error',function(){
582+
obj.$$error(new Error());
583+
expect(log.error).toHaveLength(1);
584+
});
585+
586+
it('will call $destroy',function(){
587+
obj.$destroy = jasmine.createSpy('$destroy');
588+
var error = new Error();
589+
obj.$$error(error);
590+
expect(obj.$destroy).toHaveBeenCalledWith(error);
591+
});
592+
});
593+
510594
function flushAll() {
511595
Array.prototype.slice.call(arguments, 0).forEach(function (o) {
512596
angular.isFunction(o.resolve) ? o.resolve() : o.flush();
@@ -614,4 +698,4 @@ describe('$FirebaseObject', function() {
614698
return offSpy;
615699
});
616700
}
617-
});
701+
});

tests/unit/firebase.spec.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('$firebase', function () {
1818
$get: function() {
1919
return function() {};
2020
}
21-
});
21+
}).value('NonFunctionFactory','NonFunctionValue');
2222
inject(function (_$firebase_, _$timeout_, _$rootScope_, $firebaseUtils) {
2323
$firebase = _$firebase_;
2424
$timeout = _$timeout_;
@@ -69,14 +69,28 @@ describe('$firebase', function () {
6969
it('should throw an error if factory name for arrayFactory does not exist', function() {
7070
var ref = new Firebase('Mock://');
7171
expect(function() {
72-
$firebase(ref, {arrayFactory: 'notarealarrayfactorymethod'})
72+
$firebase(ref, {arrayFactory: 'notarealarrayfactorymethod'}); //injectable by that name doesn't exist.
73+
}).toThrowError();
74+
});
75+
76+
it('should throw an error if factory name for arrayFactory exists, but is not a function', function() {
77+
var ref = new Firebase('Mock://');
78+
expect(function() {
79+
$firebase(ref, {arrayFactory: 'NonFunctionFactory'}); //injectable exists, but is not a function.
7380
}).toThrowError();
7481
});
7582

7683
it('should throw an error if factory name for objectFactory does not exist', function() {
7784
var ref = new Firebase('Mock://');
7885
expect(function() {
79-
$firebase(ref, {objectFactory: 'notarealobjectfactorymethod'})
86+
$firebase(ref, {objectFactory: 'notarealobjectfactorymethod'}); //injectable by that name doesn't exist.
87+
}).toThrowError();
88+
});
89+
90+
it('should throw an error if factory name for objectFactory exists, but is not a function', function() {
91+
var ref = new Firebase('Mock://');
92+
expect(function() {
93+
$firebase(ref, {objectFactory: 'NonFunctionFactory'}); //injectable exists, but is not a function.
8094
}).toThrowError();
8195
});
8296
});
@@ -477,7 +491,8 @@ describe('$firebase', function () {
477491
expect(function() {
478492
function fn() { return {}; }
479493
$firebase(new Firebase('Mock://').child('data'), {arrayFactory: fn}).$asArray();
480-
}).toThrowError(Error);
494+
}).toThrow(new Error('arrayFactory must return a valid array that passes ' +
495+
'angular.isArray and Array.isArray, but received "[object Object]"'));
481496
});
482497

483498
it('should contain data in ref() after load', function() {

0 commit comments

Comments
 (0)