diff --git a/package.json b/package.json index be4d21337..49f50f444 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/fire", - "version": "6.1.2", + "version": "6.1.3", "description": "The official Angular library for Firebase.", "private": true, "scripts": { diff --git a/src/auth/auth.spec.ts b/src/auth/auth.spec.ts index 0d8cc78de..802076693 100644 --- a/src/auth/auth.spec.ts +++ b/src/auth/auth.spec.ts @@ -118,20 +118,21 @@ describe('AngularFireAuth', () => { }); -const FIREBASE_APP_NAME_TOO = (Math.random() + 1).toString(36).substring(7); - describe('AngularFireAuth with different app', () => { let app: FirebaseApp; let afAuth: AngularFireAuth; + let firebaseAppName: string; beforeEach(() => { + firebaseAppName = rando(); + TestBed.configureTestingModule({ imports: [ AngularFireModule.initializeApp(COMMON_CONFIG, rando()), AngularFireAuthModule ], providers: [ - { provide: FIREBASE_APP_NAME, useValue: FIREBASE_APP_NAME_TOO }, + { provide: FIREBASE_APP_NAME, useValue: firebaseAppName }, { provide: FIREBASE_OPTIONS, useValue: COMMON_CONFIG } ] }); @@ -156,7 +157,7 @@ describe('AngularFireAuth with different app', () => { it('should have an initialized Firebase app instance member', async () => { const app = await afAuth.app; - expect(app.name).toEqual(FIREBASE_APP_NAME_TOO); + expect(app.name).toEqual(firebaseAppName); }); }); diff --git a/src/firestore/collection/collection.spec.ts b/src/firestore/collection/collection.spec.ts index 25f0ea189..3183b64a7 100644 --- a/src/firestore/collection/collection.spec.ts +++ b/src/firestore/collection/collection.spec.ts @@ -399,7 +399,7 @@ describe('AngularFirestoreCollection', () => { const ITEMS = 10; const { ref, stocks, names } = await collectionHarness(afs, ITEMS); - const sub = stocks.stateChanges(['modified']).subscribe(data => { + const sub = stocks.stateChanges(['modified']).pipe(skip(1), take(1)).subscribe(data => { sub.unsubscribe(); expect(data.length).toEqual(1); expect(data[0].payload.doc.data().price).toEqual(2); @@ -436,7 +436,7 @@ describe('AngularFirestoreCollection', () => { const ITEMS = 10; const { ref, stocks, names } = await collectionHarness(afs, ITEMS); - const sub = stocks.stateChanges(['removed']).subscribe(data => { + const sub = stocks.stateChanges(['removed']).pipe(skip(1), take(1)).subscribe(data => { sub.unsubscribe(); expect(data.length).toEqual(1); expect(data[0].type).toEqual('removed'); @@ -446,6 +446,21 @@ describe('AngularFirestoreCollection', () => { delayDelete(stocks, names[0], 400); }); + + it('stateChanges() should emit on empty collection', async (done) => { + afs.collection('EMPTY_COLLECTION').stateChanges().pipe(take(1)).subscribe(data => { + expect(data.length).toEqual(0); + done(); + }); + }); + + it('stateChanges() w/filter should emit on empty collection', async (done) => { + afs.collection('EMPTY_COLLECTION').stateChanges(['added']).pipe(take(1)).subscribe(data => { + expect(data.length).toEqual(0); + done(); + }); + }); + }); describe('auditTrail()', () => { @@ -471,7 +486,7 @@ describe('AngularFirestoreCollection', () => { const ITEMS = 10; const { ref, stocks, names } = await collectionHarness(afs, ITEMS); - const sub = stocks.auditTrail(['removed']).subscribe(data => { + const sub = stocks.auditTrail(['removed']).pipe(skip(1), take(1)).subscribe(data => { sub.unsubscribe(); expect(data.length).toEqual(1); expect(data[0].type).toEqual('removed'); diff --git a/src/firestore/collection/collection.ts b/src/firestore/collection/collection.ts index 07c4e2131..2f458d4a1 100644 --- a/src/firestore/collection/collection.ts +++ b/src/firestore/collection/collection.ts @@ -1,6 +1,6 @@ import { from, Observable } from 'rxjs'; import { fromCollectionRef } from '../observable/fromRef'; -import { filter, map, observeOn, scan } from 'rxjs/operators'; +import { filter, map, observeOn, pairwise, scan, startWith } from 'rxjs/operators'; import firebase from 'firebase/app'; import { CollectionReference, DocumentChangeAction, DocumentChangeType, DocumentData, DocumentReference, Query } from '../interfaces'; @@ -59,15 +59,19 @@ export class AngularFirestoreCollection { * your own data structure. */ stateChanges(events?: DocumentChangeType[]): Observable[]> { - if (!events || events.length === 0) { - return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe( - filter(changes => changes.length > 0), - this.afs.keepUnstableUntilFirst + let source = docChanges(this.query, this.afs.schedulers.outsideAngular); + if (events && events.length > 0) { + source = source.pipe( + map(actions => actions.filter(change => events.indexOf(change.type) > -1)) ); } - return docChanges(this.query, this.afs.schedulers.outsideAngular).pipe( - map(actions => actions.filter(change => events.indexOf(change.type) > -1)), - filter(changes => changes.length > 0), + return source.pipe( + // We want to filter out empty arrays, but always emit at first, so the developer knows + // that the collection has been resolve; even if it's empty + startWith(undefined), + pairwise(), + filter(([prior, current]) => current.length > 0 || !prior), + map(([prior, current]) => current), this.afs.keepUnstableUntilFirst ); } diff --git a/src/firestore/utils.spec.ts b/src/firestore/utils.spec.ts index 87bb97b00..f04604507 100644 --- a/src/firestore/utils.spec.ts +++ b/src/firestore/utils.spec.ts @@ -54,4 +54,4 @@ export function delayDelete(collection: AngularFirestoreCollection|firebas }, delay); } -export const rando = () => (Math.random() + 1).toString(36).substring(7); +export const rando = () => (Math.random() + 1).toString(36).split('.')[1];