mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-29 12:45:45 +08:00
[firestore] Support all onSnapshot parameter options
This commit is contained in:
@@ -57,9 +57,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
|||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
|
public void collectionOnSnapshot(String appName, String path, ReadableArray filters,
|
||||||
ReadableArray orders, ReadableMap options, String listenerId) {
|
ReadableArray orders, ReadableMap options, String listenerId,
|
||||||
|
ReadableMap queryListenOptions) {
|
||||||
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
RNFirebaseFirestoreCollectionReference ref = getCollectionForAppPath(appName, path, filters, orders, options);
|
||||||
ref.onSnapshot(listenerId);
|
ref.onSnapshot(listenerId, queryListenOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -145,9 +146,10 @@ public class RNFirebaseFirestore extends ReactContextBaseJavaModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void documentOnSnapshot(String appName, String path, String listenerId) {
|
public void documentOnSnapshot(String appName, String path, String listenerId,
|
||||||
|
ReadableMap docListenOptions) {
|
||||||
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
RNFirebaseFirestoreDocumentReference ref = getDocumentForAppPath(appName, path);
|
||||||
ref.onSnapshot(listenerId);
|
ref.onSnapshot(listenerId, docListenOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ import com.facebook.react.bridge.ReadableMap;
|
|||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.google.android.gms.tasks.OnCompleteListener;
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
import com.google.android.gms.tasks.Task;
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.firebase.firestore.DocumentListenOptions;
|
||||||
import com.google.firebase.firestore.EventListener;
|
import com.google.firebase.firestore.EventListener;
|
||||||
import com.google.firebase.firestore.FirebaseFirestoreException;
|
import com.google.firebase.firestore.FirebaseFirestoreException;
|
||||||
import com.google.firebase.firestore.ListenerRegistration;
|
import com.google.firebase.firestore.ListenerRegistration;
|
||||||
import com.google.firebase.firestore.Query;
|
import com.google.firebase.firestore.Query;
|
||||||
|
import com.google.firebase.firestore.QueryListenOptions;
|
||||||
import com.google.firebase.firestore.QuerySnapshot;
|
import com.google.firebase.firestore.QuerySnapshot;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -71,7 +73,7 @@ public class RNFirebaseFirestoreCollectionReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSnapshot(final String listenerId) {
|
public void onSnapshot(final String listenerId, final ReadableMap queryListenOptions) {
|
||||||
if (!collectionSnapshotListeners.containsKey(listenerId)) {
|
if (!collectionSnapshotListeners.containsKey(listenerId)) {
|
||||||
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
|
final EventListener<QuerySnapshot> listener = new EventListener<QuerySnapshot>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -87,7 +89,19 @@ public class RNFirebaseFirestoreCollectionReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(listener);
|
QueryListenOptions options = new QueryListenOptions();
|
||||||
|
if (queryListenOptions != null) {
|
||||||
|
if (queryListenOptions.hasKey("includeDocumentMetadataChanges")
|
||||||
|
&& queryListenOptions.getBoolean("includeDocumentMetadataChanges")) {
|
||||||
|
options.includeDocumentMetadataChanges();
|
||||||
|
}
|
||||||
|
if (queryListenOptions.hasKey("includeQueryMetadataChanges")
|
||||||
|
&& queryListenOptions.getBoolean("includeQueryMetadataChanges")) {
|
||||||
|
options.includeQueryMetadataChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenerRegistration listenerRegistration = this.query.addSnapshotListener(options, listener);
|
||||||
collectionSnapshotListeners.put(listenerId, listenerRegistration);
|
collectionSnapshotListeners.put(listenerId, listenerRegistration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import com.facebook.react.bridge.ReadableMap;
|
|||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.google.android.gms.tasks.OnCompleteListener;
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
import com.google.android.gms.tasks.Task;
|
import com.google.android.gms.tasks.Task;
|
||||||
|
import com.google.firebase.firestore.DocumentListenOptions;
|
||||||
import com.google.firebase.firestore.DocumentReference;
|
import com.google.firebase.firestore.DocumentReference;
|
||||||
import com.google.firebase.firestore.DocumentSnapshot;
|
import com.google.firebase.firestore.DocumentSnapshot;
|
||||||
import com.google.firebase.firestore.EventListener;
|
import com.google.firebase.firestore.EventListener;
|
||||||
@@ -85,7 +86,7 @@ public class RNFirebaseFirestoreDocumentReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSnapshot(final String listenerId) {
|
public void onSnapshot(final String listenerId, final ReadableMap docListenOptions) {
|
||||||
if (!documentSnapshotListeners.containsKey(listenerId)) {
|
if (!documentSnapshotListeners.containsKey(listenerId)) {
|
||||||
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
|
final EventListener<DocumentSnapshot> listener = new EventListener<DocumentSnapshot>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -101,7 +102,11 @@ public class RNFirebaseFirestoreDocumentReference {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(listener);
|
DocumentListenOptions options = new DocumentListenOptions();
|
||||||
|
if (docListenOptions != null && docListenOptions.hasKey("includeMetadataChanges") && docListenOptions.getBoolean("includeMetadataChanges")) {
|
||||||
|
options.includeMetadataChanges();
|
||||||
|
}
|
||||||
|
ListenerRegistration listenerRegistration = this.ref.addSnapshotListener(options, listener);
|
||||||
documentSnapshotListeners.put(listenerId, listenerRegistration);
|
documentSnapshotListeners.put(listenerId, listenerRegistration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,10 @@ RCT_EXPORT_METHOD(collectionOnSnapshot:(NSString *) appName
|
|||||||
filters:(NSArray *) filters
|
filters:(NSArray *) filters
|
||||||
orders:(NSArray *) orders
|
orders:(NSArray *) orders
|
||||||
options:(NSDictionary *) options
|
options:(NSDictionary *) options
|
||||||
listenerId:(nonnull NSString *) listenerId) {
|
listenerId:(nonnull NSString *) listenerId
|
||||||
|
queryListenOptions:(NSDictionary *) queryListenOptions) {
|
||||||
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
|
RNFirebaseFirestoreCollectionReference *ref = [self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options];
|
||||||
[ref onSnapshot:listenerId];
|
[ref onSnapshot:listenerId queryListenOptions:queryListenOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
|
RCT_EXPORT_METHOD(documentBatch:(NSString *) appName
|
||||||
@@ -128,9 +129,10 @@ RCT_EXPORT_METHOD(documentOffSnapshot:(NSString *) appName
|
|||||||
|
|
||||||
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
|
RCT_EXPORT_METHOD(documentOnSnapshot:(NSString *) appName
|
||||||
path:(NSString *) path
|
path:(NSString *) path
|
||||||
listenerId:(nonnull NSString *) listenerId) {
|
listenerId:(nonnull NSString *) listenerId
|
||||||
|
docListenOptions:(NSDictionary *) docListenOptions) {
|
||||||
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
|
RNFirebaseFirestoreDocumentReference *ref = [self getDocumentForAppPath:appName path:path];
|
||||||
[ref onSnapshot:listenerId];
|
[ref onSnapshot:listenerId docListenOptions:docListenOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
|
RCT_EXPORT_METHOD(documentSet:(NSString *) appName
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
|
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options;
|
||||||
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
+ (void)offSnapshot:(NSString *)listenerId;
|
+ (void)offSnapshot:(NSString *)listenerId;
|
||||||
- (void)onSnapshot:(NSString *)listenerId;
|
- (void)onSnapshot:(NSString *)listenerId queryListenOptions:(NSDictionary *) queryListenOptions;
|
||||||
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot;
|
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ static NSMutableDictionary *_listeners;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onSnapshot:(NSString *) listenerId {
|
- (void)onSnapshot:(NSString *) listenerId
|
||||||
|
queryListenOptions:(NSDictionary *) queryListenOptions {
|
||||||
if (_listeners[listenerId] == nil) {
|
if (_listeners[listenerId] == nil) {
|
||||||
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -63,7 +64,18 @@ static NSMutableDictionary *_listeners;
|
|||||||
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
id<FIRListenerRegistration> listener = [_query addSnapshotListener:listenerBlock];
|
|
||||||
|
FIRQueryListenOptions *options = [[FIRQueryListenOptions alloc] init];
|
||||||
|
if (queryListenOptions) {
|
||||||
|
if (queryListenOptions[@"includeDocumentMetadataChanges"]) {
|
||||||
|
[options includeDocumentMetadataChanges:TRUE];
|
||||||
|
}
|
||||||
|
if (queryListenOptions[@"includeQueryMetadataChanges"]) {
|
||||||
|
[options includeQueryMetadataChanges:TRUE];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithOptions:options listener:listenerBlock];
|
||||||
_listeners[listenerId] = listener;
|
_listeners[listenerId] = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
- (void)delete:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
+ (void)offSnapshot:(NSString *)listenerId;
|
+ (void)offSnapshot:(NSString *)listenerId;
|
||||||
- (void)onSnapshot:(NSString *)listenerId;
|
- (void)onSnapshot:(NSString *)listenerId docListenOptions:(NSDictionary *) docListenOptions;
|
||||||
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
|
||||||
- (BOOL)hasListeners;
|
- (BOOL)hasListeners;
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ static NSMutableDictionary *_listeners;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)onSnapshot:(NSString *) listenerId {
|
- (void)onSnapshot:(NSString *) listenerId
|
||||||
|
docListenOptions:(NSDictionary *) docListenOptions {
|
||||||
if (_listeners[listenerId] == nil) {
|
if (_listeners[listenerId] == nil) {
|
||||||
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -75,8 +76,11 @@ static NSMutableDictionary *_listeners;
|
|||||||
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
|
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
FIRDocumentListenOptions *options = [[FIRDocumentListenOptions alloc] init];
|
||||||
id<FIRListenerRegistration> listener = [_ref addSnapshotListener:listenerBlock];
|
if (docListenOptions && docListenOptions[@"includeMetadataChanges"]) {
|
||||||
|
[options includeMetadataChanges:TRUE];
|
||||||
|
}
|
||||||
|
id<FIRListenerRegistration> listener = [_ref addSnapshotListenerWithOptions:options listener:listenerBlock];
|
||||||
_listeners[listenerId] = listener;
|
_listeners[listenerId] = listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,21 @@
|
|||||||
import CollectionReference from './CollectionReference';
|
import CollectionReference from './CollectionReference';
|
||||||
import DocumentSnapshot from './DocumentSnapshot';
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
import Path from './Path';
|
import Path from './Path';
|
||||||
import { firestoreAutoId } from '../../utils';
|
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||||
|
|
||||||
export type WriteOptions = {
|
export type WriteOptions = {
|
||||||
merge?: boolean,
|
merge?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DocumentListenOptions = {
|
||||||
|
includeMetadataChanges: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Observer = {
|
||||||
|
next: (DocumentSnapshot) => void,
|
||||||
|
error?: (Object) => void,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class DocumentReference
|
* @class DocumentReference
|
||||||
*/
|
*/
|
||||||
@@ -60,13 +69,64 @@ export default class DocumentReference {
|
|||||||
.then(result => new DocumentSnapshot(this._firestore, result));
|
.then(result => new DocumentSnapshot(this._firestore, result));
|
||||||
}
|
}
|
||||||
|
|
||||||
onSnapshot(onNext: Function, onError?: Function): () => void {
|
onSnapshot(
|
||||||
// TODO: Validation
|
optionsOrObserverOrOnNext: DocumentListenOptions | Observer | (DocumentSnapshot) => void,
|
||||||
|
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
|
||||||
|
onError?: (Object) => void
|
||||||
|
) {
|
||||||
|
let observer = {};
|
||||||
|
let docListenOptions = {};
|
||||||
|
// Called with: onNext, ?onError
|
||||||
|
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||||
|
observer.next = optionsOrObserverOrOnNext;
|
||||||
|
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a valid function.');
|
||||||
|
}
|
||||||
|
observer.error = observerOrOnNextOrOnError;
|
||||||
|
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
|
||||||
|
// Called with: Observer
|
||||||
|
if (optionsOrObserverOrOnNext.next) {
|
||||||
|
if (isFunction(optionsOrObserverOrOnNext.next)) {
|
||||||
|
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
|
||||||
|
}
|
||||||
|
observer = optionsOrObserverOrOnNext;
|
||||||
|
} else {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
|
||||||
|
}
|
||||||
|
} else if (optionsOrObserverOrOnNext.includeMetadataChanges) {
|
||||||
|
docListenOptions = optionsOrObserverOrOnNext;
|
||||||
|
// Called with: Options, onNext, ?onError
|
||||||
|
if (isFunction(observerOrOnNextOrOnError)) {
|
||||||
|
observer.next = observerOrOnNextOrOnError;
|
||||||
|
if (onError && !isFunction(onError)) {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Third argument must be a valid function.');
|
||||||
|
}
|
||||||
|
observer.error = onError;
|
||||||
|
// Called with Options, Observer
|
||||||
|
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
|
||||||
|
if (isFunction(observerOrOnNextOrOnError.next)) {
|
||||||
|
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Observer.error must be a valid function.');
|
||||||
|
}
|
||||||
|
observer = observerOrOnNextOrOnError;
|
||||||
|
} else {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Observer.next must be a valid function.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Second argument must be a function or observer.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: First argument must be a function, observer or options.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('DocumentReference.onSnapshot failed: Called with invalid arguments.');
|
||||||
|
}
|
||||||
const listenerId = firestoreAutoId();
|
const listenerId = firestoreAutoId();
|
||||||
|
|
||||||
const listener = (nativeDocumentSnapshot) => {
|
const listener = (nativeDocumentSnapshot) => {
|
||||||
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
|
const documentSnapshot = new DocumentSnapshot(this, nativeDocumentSnapshot);
|
||||||
onNext(documentSnapshot);
|
observer.next(documentSnapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen to snapshot events
|
// Listen to snapshot events
|
||||||
@@ -76,16 +136,16 @@ export default class DocumentReference {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Listen for snapshot error events
|
// Listen for snapshot error events
|
||||||
if (onError) {
|
if (observer.error) {
|
||||||
this._firestore.on(
|
this._firestore.on(
|
||||||
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
|
this._firestore._getAppEventName(`onDocumentSnapshotError:${listenerId}`),
|
||||||
onError,
|
observer.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the native listener
|
// Add the native listener
|
||||||
this._firestore._native
|
this._firestore._native
|
||||||
.documentOnSnapshot(this.path, listenerId);
|
.documentOnSnapshot(this.path, listenerId, docListenOptions);
|
||||||
|
|
||||||
// Return an unsubscribe method
|
// Return an unsubscribe method
|
||||||
return this._offDocumentSnapshot.bind(this, listenerId, listener);
|
return this._offDocumentSnapshot.bind(this, listenerId, listener);
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
import DocumentSnapshot from './DocumentSnapshot';
|
import DocumentSnapshot from './DocumentSnapshot';
|
||||||
import Path from './Path';
|
import Path from './Path';
|
||||||
import QuerySnapshot from './QuerySnapshot';
|
import QuerySnapshot from './QuerySnapshot';
|
||||||
import INTERNALS from '../../internals';
|
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||||
import { firestoreAutoId } from '../../utils';
|
|
||||||
|
|
||||||
const DIRECTIONS = {
|
const DIRECTIONS = {
|
||||||
ASC: 'ASCENDING',
|
ASC: 'ASCENDING',
|
||||||
@@ -44,6 +43,15 @@ type QueryOptions = {
|
|||||||
startAt?: any[],
|
startAt?: any[],
|
||||||
}
|
}
|
||||||
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
||||||
|
type QueryListenOptions = {
|
||||||
|
includeQueryMetadataChanges: boolean,
|
||||||
|
includeQueryMetadataChanges: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Observer = {
|
||||||
|
next: (DocumentSnapshot) => void,
|
||||||
|
error?: (Object) => void,
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Query
|
* @class Query
|
||||||
@@ -116,13 +124,65 @@ export default class Query {
|
|||||||
this._fieldOrders, options);
|
this._fieldOrders, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
onSnapshot(
|
||||||
// TODO: Validation
|
optionsOrObserverOrOnNext: QueryListenOptions | Observer | (DocumentSnapshot) => void,
|
||||||
|
observerOrOnNextOrOnError?: Observer | (DocumentSnapshot) => void | (Object) => void,
|
||||||
|
onError?: (Object) => void,
|
||||||
|
) {
|
||||||
|
let observer = {};
|
||||||
|
let queryListenOptions = {};
|
||||||
|
// Called with: onNext, ?onError
|
||||||
|
if (isFunction(optionsOrObserverOrOnNext)) {
|
||||||
|
observer.next = optionsOrObserverOrOnNext;
|
||||||
|
if (observerOrOnNextOrOnError && !isFunction(observerOrOnNextOrOnError)) {
|
||||||
|
throw new Error('Query.onSnapshot failed: Second argument must be a valid function.');
|
||||||
|
}
|
||||||
|
observer.error = observerOrOnNextOrOnError;
|
||||||
|
} else if (optionsOrObserverOrOnNext && isObject(optionsOrObserverOrOnNext)) {
|
||||||
|
// Called with: Observer
|
||||||
|
if (optionsOrObserverOrOnNext.next) {
|
||||||
|
if (isFunction(optionsOrObserverOrOnNext.next)) {
|
||||||
|
if (optionsOrObserverOrOnNext.error && !isFunction(optionsOrObserverOrOnNext.error)) {
|
||||||
|
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
|
||||||
|
}
|
||||||
|
observer = optionsOrObserverOrOnNext;
|
||||||
|
} else {
|
||||||
|
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
|
||||||
|
}
|
||||||
|
} else if (optionsOrObserverOrOnNext.includeDocumentMetadataChanges || optionsOrObserverOrOnNext.includeQueryMetadataChanges) {
|
||||||
|
queryListenOptions = optionsOrObserverOrOnNext;
|
||||||
|
// Called with: Options, onNext, ?onError
|
||||||
|
if (isFunction(observerOrOnNextOrOnError)) {
|
||||||
|
observer.next = observerOrOnNextOrOnError;
|
||||||
|
if (onError && !isFunction(onError)) {
|
||||||
|
throw new Error('Query.onSnapshot failed: Third argument must be a valid function.');
|
||||||
|
}
|
||||||
|
observer.error = onError;
|
||||||
|
// Called with Options, Observer
|
||||||
|
} else if (observerOrOnNextOrOnError && isObject(observerOrOnNextOrOnError) && observerOrOnNextOrOnError.next) {
|
||||||
|
if (isFunction(observerOrOnNextOrOnError.next)) {
|
||||||
|
if (observerOrOnNextOrOnError.error && !isFunction(observerOrOnNextOrOnError.error)) {
|
||||||
|
throw new Error('Query.onSnapshot failed: Observer.error must be a valid function.');
|
||||||
|
}
|
||||||
|
observer = observerOrOnNextOrOnError;
|
||||||
|
} else {
|
||||||
|
throw new Error('Query.onSnapshot failed: Observer.next must be a valid function.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Query.onSnapshot failed: Second argument must be a function or observer.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Query.onSnapshot failed: First argument must be a function, observer or options.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error('Query.onSnapshot failed: Called with invalid arguments.');
|
||||||
|
}
|
||||||
|
|
||||||
const listenerId = firestoreAutoId();
|
const listenerId = firestoreAutoId();
|
||||||
|
|
||||||
const listener = (nativeQuerySnapshot) => {
|
const listener = (nativeQuerySnapshot) => {
|
||||||
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
|
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
|
||||||
onNext(querySnapshot);
|
observer.next(querySnapshot);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Listen to snapshot events
|
// Listen to snapshot events
|
||||||
@@ -132,10 +192,10 @@ export default class Query {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Listen for snapshot error events
|
// Listen for snapshot error events
|
||||||
if (onError) {
|
if (observer.error) {
|
||||||
this._firestore.on(
|
this._firestore.on(
|
||||||
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
|
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
|
||||||
onError,
|
observer.error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +206,8 @@ export default class Query {
|
|||||||
this._fieldFilters,
|
this._fieldFilters,
|
||||||
this._fieldOrders,
|
this._fieldOrders,
|
||||||
this._queryOptions,
|
this._queryOptions,
|
||||||
listenerId
|
listenerId,
|
||||||
|
queryListenOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Return an unsubscribe method
|
// Return an unsubscribe method
|
||||||
|
|||||||
@@ -129,30 +129,30 @@ PODS:
|
|||||||
- nanopb/decode (0.3.8)
|
- nanopb/decode (0.3.8)
|
||||||
- nanopb/encode (0.3.8)
|
- nanopb/encode (0.3.8)
|
||||||
- Protobuf (3.4.0)
|
- Protobuf (3.4.0)
|
||||||
- React (0.49.0-rc.6):
|
- React (0.49.1):
|
||||||
- React/Core (= 0.49.0-rc.6)
|
- React/Core (= 0.49.1)
|
||||||
- React/BatchedBridge (0.49.0-rc.6):
|
- React/BatchedBridge (0.49.1):
|
||||||
- React/Core
|
- React/Core
|
||||||
- React/cxxreact_legacy
|
- React/cxxreact_legacy
|
||||||
- React/Core (0.49.0-rc.6):
|
- React/Core (0.49.1):
|
||||||
- yoga (= 0.49.0-rc.6.React)
|
- yoga (= 0.49.1.React)
|
||||||
- React/cxxreact_legacy (0.49.0-rc.6):
|
- React/cxxreact_legacy (0.49.1):
|
||||||
- React/jschelpers_legacy
|
- React/jschelpers_legacy
|
||||||
- React/fishhook (0.49.0-rc.6)
|
- React/fishhook (0.49.1)
|
||||||
- React/jschelpers_legacy (0.49.0-rc.6)
|
- React/jschelpers_legacy (0.49.1)
|
||||||
- React/RCTBlob (0.49.0-rc.6):
|
- React/RCTBlob (0.49.1):
|
||||||
- React/Core
|
- React/Core
|
||||||
- React/RCTNetwork (0.49.0-rc.6):
|
- React/RCTNetwork (0.49.1):
|
||||||
- React/Core
|
- React/Core
|
||||||
- React/RCTText (0.49.0-rc.6):
|
- React/RCTText (0.49.1):
|
||||||
- React/Core
|
- React/Core
|
||||||
- React/RCTWebSocket (0.49.0-rc.6):
|
- React/RCTWebSocket (0.49.1):
|
||||||
- React/Core
|
- React/Core
|
||||||
- React/fishhook
|
- React/fishhook
|
||||||
- React/RCTBlob
|
- React/RCTBlob
|
||||||
- RNFirebase (3.0.0):
|
- RNFirebase (3.0.0):
|
||||||
- React
|
- React
|
||||||
- yoga (0.49.0-rc.6.React)
|
- yoga (0.49.1.React)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- Firebase/AdMob
|
- Firebase/AdMob
|
||||||
@@ -207,9 +207,9 @@ SPEC CHECKSUMS:
|
|||||||
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
|
leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404
|
||||||
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3
|
||||||
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
Protobuf: 03eef2ee0b674770735cf79d9c4d3659cf6908e8
|
||||||
React: e6ef6a41ec6dd1b7941417d60ca582bf5e9c739d
|
React: cf892fb84b7d06bf5fea7f328e554c6dcabe85ee
|
||||||
RNFirebase: 2ceda3aef595ea1379bf5bfcc1cd9ee7d6c05d3b
|
RNFirebase: 901a473c68fcbaa28125c56a911923f2fbe5d61b
|
||||||
yoga: f9485d2ebf0ca773db2d727ea71b1aa8c9f3e075
|
yoga: 3abf02d6d9aeeb139b4c930eb1367feae690a35a
|
||||||
|
|
||||||
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
|
PODFILE CHECKSUM: b5674be55653f5dda937c8b794d0479900643d45
|
||||||
|
|
||||||
|
|||||||
@@ -53,262 +53,360 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('calls callback with the initial data and then when document changes', () => {
|
it('calls callback with the initial data and then when document changes', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
const currentDocValue = { name: 'doc1' };
|
||||||
const currentDocValue = { name: 'doc1' };
|
const newDocValue = { name: 'updated' };
|
||||||
const newDocValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
const callback = sinon.spy();
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
let unsubscribe;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
snapshot.forEach(doc => callback(doc.data()));
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDocValue);
|
|
||||||
|
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
|
||||||
await docRef.set(newDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
|
|
||||||
callback.should.be.calledWith(newDocValue);
|
|
||||||
callback.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribe();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('calls callback with the initial data and then when document is added', () => {
|
it('calls callback with the initial data and then when document is added', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
const currentDocValue = { name: 'doc1' };
|
||||||
const currentDocValue = { name: 'doc1' };
|
const newDocValue = { name: 'updated' };
|
||||||
const newDocValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
const callback = sinon.spy();
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
let unsubscribe;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
snapshot.forEach(doc => callback(doc.data()));
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDocValue);
|
|
||||||
|
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc2');
|
|
||||||
await docRef.set(newDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDocValue);
|
|
||||||
callback.should.be.calledWith(newDocValue);
|
|
||||||
callback.should.be.calledThrice();
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribe();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc2');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
callback.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
const currentDocValue = { name: 'doc1' };
|
||||||
const currentDocValue = { name: 'doc1' };
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
const callback = sinon.spy();
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
let unsubscribe;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
unsubscribe = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(currentDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledOnce(); // Callback is not called again
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('allows binding multiple callbacks to the same ref', async () => {
|
||||||
|
// Setup
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackA(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackB(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDocValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDocValue);
|
||||||
|
callbackB.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
unsubscribeB();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('listener stops listening when unsubscribed', async () => {
|
||||||
|
// Setup
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackA(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
||||||
|
snapshot.forEach(doc => callbackB(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDocValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDocValue);
|
||||||
|
callbackB.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Unsubscribe A
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
|
||||||
|
await docRef.set(currentDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Unsubscribe B
|
||||||
|
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('supports options and callback', async () => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = collectionRef.onSnapshot({ includeQueryMetadataChanges: true, includeDocumentMetadataChanges: true }, (snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('supports observer', async () => {
|
||||||
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
|
const currentDocValue = { name: 'doc1' };
|
||||||
|
const newDocValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
const observer = {
|
||||||
|
next: (snapshot) => {
|
||||||
snapshot.forEach(doc => callback(doc.data()));
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
},
|
||||||
});
|
};
|
||||||
|
unsubscribe = collectionRef.onSnapshot(observer);
|
||||||
callback.should.be.calledWith(currentDocValue);
|
|
||||||
|
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
|
||||||
await docRef.set(currentDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
|
|
||||||
callback.should.be.calledOnce(); // Callback is not called again
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribe();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDocValue);
|
||||||
|
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('allows binding multiple callbacks to the same ref', () => {
|
it('supports options and observer', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const collectionRef = firebase.native.firestore().collection('document-tests');
|
||||||
// Setup
|
const currentDocValue = { name: 'doc1' };
|
||||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
const newDocValue = { name: 'updated' };
|
||||||
const currentDocValue = { name: 'doc1' };
|
|
||||||
const newDocValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callbackA = sinon.spy();
|
const callback = sinon.spy();
|
||||||
const callbackB = sinon.spy();
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
let unsubscribeA;
|
|
||||||
let unsubscribeB;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
const observer = {
|
||||||
snapshot.forEach(doc => callbackA(doc.data()));
|
next: (snapshot) => {
|
||||||
|
snapshot.forEach(doc => callback(doc.data()));
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
},
|
||||||
});
|
};
|
||||||
await new Promise((resolve2) => {
|
unsubscribe = collectionRef.onSnapshot({ includeQueryMetadataChanges: true, includeDocumentMetadataChanges: true }, observer);
|
||||||
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
|
||||||
snapshot.forEach(doc => callbackB(doc.data()));
|
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(currentDocValue);
|
|
||||||
callbackA.should.be.calledOnce();
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDocValue);
|
|
||||||
callbackB.should.be.calledOnce();
|
|
||||||
|
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
|
||||||
await docRef.set(newDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(newDocValue);
|
|
||||||
callbackB.should.be.calledWith(newDocValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribeA();
|
|
||||||
unsubscribeB();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
callback.should.be.calledWith(currentDocValue);
|
||||||
it('listener stops listening when unsubscribed', () => {
|
|
||||||
return new Promise(async (resolve) => {
|
|
||||||
// Setup
|
|
||||||
const collectionRef = firebase.native.firestore().collection('document-tests');
|
|
||||||
const currentDocValue = { name: 'doc1' };
|
|
||||||
const newDocValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callbackA = sinon.spy();
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const callbackB = sinon.spy();
|
await docRef.set(newDocValue);
|
||||||
|
|
||||||
// Test
|
await new Promise((resolve2) => {
|
||||||
let unsubscribeA;
|
setTimeout(() => resolve2(), 5);
|
||||||
let unsubscribeB;
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
unsubscribeA = collectionRef.onSnapshot((snapshot) => {
|
|
||||||
snapshot.forEach(doc => callbackA(doc.data()));
|
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
unsubscribeB = collectionRef.onSnapshot((snapshot) => {
|
|
||||||
snapshot.forEach(doc => callbackB(doc.data()));
|
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(currentDocValue);
|
|
||||||
callbackA.should.be.calledOnce();
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDocValue);
|
|
||||||
callbackB.should.be.calledOnce();
|
|
||||||
|
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
|
||||||
await docRef.set(newDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(newDocValue);
|
|
||||||
callbackB.should.be.calledWith(newDocValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Unsubscribe A
|
|
||||||
|
|
||||||
unsubscribeA();
|
|
||||||
|
|
||||||
await docRef.set(currentDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDocValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledThrice();
|
|
||||||
|
|
||||||
// Unsubscribe B
|
|
||||||
|
|
||||||
unsubscribeB();
|
|
||||||
|
|
||||||
await docRef.set(newDocValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledThrice();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDocValue);
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import sinon from 'sinon';
|
|||||||
import 'should-sinon';
|
import 'should-sinon';
|
||||||
import should from 'should';
|
import should from 'should';
|
||||||
|
|
||||||
function collectionReferenceTests({ describe, it, context, firebase }) {
|
function documentReferenceTests({ describe, it, context, firebase }) {
|
||||||
describe('DocumentReference', () => {
|
describe('DocumentReference', () => {
|
||||||
context('class', () => {
|
context('class', () => {
|
||||||
it('should return instance methods', () => {
|
it('should return instance methods', () => {
|
||||||
@@ -29,219 +29,324 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('calls callback with the initial data and then when value changes', () => {
|
it('calls callback with the initial data and then when value changes', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const currentDataValue = { name: 'doc1' };
|
||||||
const currentDataValue = { name: 'doc1' };
|
const newDataValue = { name: 'updated' };
|
||||||
const newDataValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
const callback = sinon.spy();
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
let unsubscribe;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribe = docRef.onSnapshot((snapshot) => {
|
unsubscribe = docRef.onSnapshot((snapshot) => {
|
||||||
callback(snapshot.data());
|
callback(snapshot.data());
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDataValue);
|
|
||||||
|
|
||||||
// Update the document
|
|
||||||
|
|
||||||
await docRef.set(newDataValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Assertions
|
|
||||||
|
|
||||||
callback.should.be.calledWith(newDataValue);
|
|
||||||
callback.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribe();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
// Update the document
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
it('doesn\'t call callback when the ref is updated with the same value', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const currentDataValue = { name: 'doc1' };
|
||||||
const currentDataValue = { name: 'doc1' };
|
|
||||||
|
|
||||||
const callback = sinon.spy();
|
const callback = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
|
||||||
|
let unsubscribe;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribe = docRef.onSnapshot((snapshot) => {
|
||||||
|
callback(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
await docRef.set(currentDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledOnce(); // Callback is not called again
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('allows binding multiple callbacks to the same ref', async () => {
|
||||||
|
// Setup
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackA(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackB(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDataValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDataValue);
|
||||||
|
callbackB.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
unsubscribeB();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('listener stops listening when unsubscribed', async () => {
|
||||||
|
// Setup
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callbackA = sinon.spy();
|
||||||
|
const callbackB = sinon.spy();
|
||||||
|
|
||||||
|
// Test
|
||||||
|
let unsubscribeA;
|
||||||
|
let unsubscribeB;
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackA(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
||||||
|
callbackB(snapshot.data());
|
||||||
|
resolve2();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(currentDataValue);
|
||||||
|
callbackA.should.be.calledOnce();
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
callbackB.should.be.calledOnce();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledWith(newDataValue);
|
||||||
|
callbackB.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Unsubscribe A
|
||||||
|
|
||||||
|
unsubscribeA();
|
||||||
|
|
||||||
|
await docRef.set(currentDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackB.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
|
||||||
|
// Unsubscribe B
|
||||||
|
|
||||||
|
unsubscribeB();
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbackA.should.be.calledTwice();
|
||||||
|
callbackB.should.be.calledThrice();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
context('onSnapshot()', () => {
|
||||||
|
it('supports options and callbacks', async () => {
|
||||||
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
|
const currentDataValue = { name: 'doc1' };
|
||||||
|
const newDataValue = { name: 'updated' };
|
||||||
|
|
||||||
|
const callback = sinon.spy();
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
|
|
||||||
let unsubscribe;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribe = docRef.onSnapshot((snapshot) => {
|
unsubscribe = docRef.onSnapshot({ includeMetadataChanges: true }, (snapshot) => {
|
||||||
callback(snapshot.data());
|
callback(snapshot.data());
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
callback.should.be.calledWith(currentDataValue);
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
await docRef.set(currentDataValue);
|
// Update the document
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
await docRef.set(newDataValue);
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
// Assertions
|
// Assertions
|
||||||
|
|
||||||
callback.should.be.calledOnce(); // Callback is not called again
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
// Tear down
|
// Tear down
|
||||||
|
|
||||||
unsubscribe();
|
unsubscribe();
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('allows binding multiple callbacks to the same ref', () => {
|
it('supports observer', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
// Setup
|
const currentDataValue = { name: 'doc1' };
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const newDataValue = { name: 'updated' };
|
||||||
const currentDataValue = { name: 'doc1' };
|
|
||||||
const newDataValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callbackA = sinon.spy();
|
const callback = sinon.spy();
|
||||||
const callbackB = sinon.spy();
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
let unsubscribeA;
|
|
||||||
let unsubscribeB;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
const observer = {
|
||||||
callbackA(snapshot.data());
|
next: (snapshot) => {
|
||||||
|
callback(snapshot.data());
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
},
|
||||||
});
|
};
|
||||||
|
unsubscribe = docRef.onSnapshot(observer);
|
||||||
await new Promise((resolve2) => {
|
|
||||||
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
|
||||||
callbackB(snapshot.data());
|
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(currentDataValue);
|
|
||||||
callbackA.should.be.calledOnce();
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDataValue);
|
|
||||||
callbackB.should.be.calledOnce();
|
|
||||||
|
|
||||||
await docRef.set(newDataValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(newDataValue);
|
|
||||||
callbackB.should.be.calledWith(newDataValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Tear down
|
|
||||||
|
|
||||||
unsubscribeA();
|
|
||||||
unsubscribeB();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
// Update the document
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
callback.should.be.calledTwice();
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('onSnapshot()', () => {
|
context('onSnapshot()', () => {
|
||||||
it('listener stops listening when unsubscribed', () => {
|
it('supports options and observer', async () => {
|
||||||
return new Promise(async (resolve) => {
|
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
||||||
// Setup
|
const currentDataValue = { name: 'doc1' };
|
||||||
const docRef = firebase.native.firestore().doc('document-tests/doc1');
|
const newDataValue = { name: 'updated' };
|
||||||
const currentDataValue = { name: 'doc1' };
|
|
||||||
const newDataValue = { name: 'updated' };
|
|
||||||
|
|
||||||
const callbackA = sinon.spy();
|
const callback = sinon.spy();
|
||||||
const callbackB = sinon.spy();
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
let unsubscribeA;
|
|
||||||
let unsubscribeB;
|
let unsubscribe;
|
||||||
await new Promise((resolve2) => {
|
await new Promise((resolve2) => {
|
||||||
unsubscribeA = docRef.onSnapshot((snapshot) => {
|
const observer = {
|
||||||
callbackA(snapshot.data());
|
next: (snapshot) => {
|
||||||
|
callback(snapshot.data());
|
||||||
resolve2();
|
resolve2();
|
||||||
});
|
},
|
||||||
});
|
};
|
||||||
|
unsubscribe = docRef.onSnapshot({ includeMetadataChanges: true }, observer);
|
||||||
await new Promise((resolve2) => {
|
|
||||||
unsubscribeB = docRef.onSnapshot((snapshot) => {
|
|
||||||
callbackB(snapshot.data());
|
|
||||||
resolve2();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(currentDataValue);
|
|
||||||
callbackA.should.be.calledOnce();
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDataValue);
|
|
||||||
callbackB.should.be.calledOnce();
|
|
||||||
|
|
||||||
await docRef.set(newDataValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledWith(newDataValue);
|
|
||||||
callbackB.should.be.calledWith(newDataValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledTwice();
|
|
||||||
|
|
||||||
// Unsubscribe A
|
|
||||||
|
|
||||||
unsubscribeA();
|
|
||||||
|
|
||||||
await docRef.set(currentDataValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackB.should.be.calledWith(currentDataValue);
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledThrice();
|
|
||||||
|
|
||||||
// Unsubscribe B
|
|
||||||
|
|
||||||
unsubscribeB();
|
|
||||||
|
|
||||||
await docRef.set(newDataValue);
|
|
||||||
|
|
||||||
await new Promise((resolve2) => {
|
|
||||||
setTimeout(() => resolve2(), 5);
|
|
||||||
});
|
|
||||||
|
|
||||||
callbackA.should.be.calledTwice();
|
|
||||||
callbackB.should.be.calledThrice();
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callback.should.be.calledWith(currentDataValue);
|
||||||
|
|
||||||
|
// Update the document
|
||||||
|
|
||||||
|
await docRef.set(newDataValue);
|
||||||
|
|
||||||
|
await new Promise((resolve2) => {
|
||||||
|
setTimeout(() => resolve2(), 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assertions
|
||||||
|
|
||||||
|
callback.should.be.calledWith(newDataValue);
|
||||||
|
|
||||||
|
// Tear down
|
||||||
|
|
||||||
|
unsubscribe();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -296,4 +401,4 @@ function collectionReferenceTests({ describe, it, context, firebase }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default collectionReferenceTests;
|
export default documentReferenceTests;
|
||||||
|
|||||||
Reference in New Issue
Block a user