mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-01-12 22:50:20 +08:00
Merge pull request #1509 from invertase/firestore-array-contains
[firestore] 'array-contains' query support
This commit is contained in:
@@ -189,6 +189,9 @@ class RNFirebaseFirestoreCollectionReference {
|
||||
case "LESS_THAN_OR_EQUAL":
|
||||
query = query.whereLessThanOrEqualTo(fieldPath, value);
|
||||
break;
|
||||
case "ARRAY_CONTAINS":
|
||||
query = query.whereArrayContains(fieldPath, value);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ReadableArray fieldPathElements = fieldPathMap.getArray("elements");
|
||||
@@ -213,6 +216,9 @@ class RNFirebaseFirestoreCollectionReference {
|
||||
case "LESS_THAN_OR_EQUAL":
|
||||
query = query.whereLessThanOrEqualTo(fieldPath, value);
|
||||
break;
|
||||
case "ARRAY_CONTAINS":
|
||||
query = query.whereArrayContains(fieldPath, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,254 +6,264 @@
|
||||
|
||||
static NSMutableDictionary *_listeners;
|
||||
|
||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *) emitter
|
||||
appDisplayName:(NSString *) appDisplayName
|
||||
path:(NSString *) path
|
||||
filters:(NSArray *) filters
|
||||
orders:(NSArray *) orders
|
||||
options:(NSDictionary *) options {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_emitter = emitter;
|
||||
_appDisplayName = appDisplayName;
|
||||
_path = path;
|
||||
_filters = filters;
|
||||
_orders = orders;
|
||||
_options = options;
|
||||
_query = [self buildQuery];
|
||||
}
|
||||
// Initialise the static listeners object if required
|
||||
if (!_listeners) {
|
||||
_listeners = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
|
||||
appDisplayName:(NSString *)appDisplayName
|
||||
path:(NSString *)path
|
||||
filters:(NSArray *)filters
|
||||
orders:(NSArray *)orders
|
||||
options:(NSDictionary *)options {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_emitter = emitter;
|
||||
_appDisplayName = appDisplayName;
|
||||
_path = path;
|
||||
_filters = filters;
|
||||
_orders = orders;
|
||||
_options = options;
|
||||
_query = [self buildQuery];
|
||||
}
|
||||
// Initialise the static listeners object if required
|
||||
if (!_listeners) {
|
||||
_listeners = [[NSMutableDictionary alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)get:(NSDictionary *) getOptions
|
||||
resolver:(RCTPromiseResolveBlock) resolve
|
||||
rejecter:(RCTPromiseRejectBlock) reject {
|
||||
FIRFirestoreSource source;
|
||||
if (getOptions && getOptions[@"source"]) {
|
||||
if ([getOptions[@"source"] isEqualToString:@"server"]) {
|
||||
source = FIRFirestoreSourceServer;
|
||||
} else if ([getOptions[@"source"] isEqualToString:@"cache"]) {
|
||||
source = FIRFirestoreSourceCache;
|
||||
} else {
|
||||
source = FIRFirestoreSourceDefault;
|
||||
}
|
||||
- (void)get:(NSDictionary *)getOptions
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject {
|
||||
FIRFirestoreSource source;
|
||||
if (getOptions && getOptions[@"source"]) {
|
||||
if ([getOptions[@"source"] isEqualToString:@"server"]) {
|
||||
source = FIRFirestoreSourceServer;
|
||||
} else if ([getOptions[@"source"] isEqualToString:@"cache"]) {
|
||||
source = FIRFirestoreSourceCache;
|
||||
} else {
|
||||
source = FIRFirestoreSourceDefault;
|
||||
source = FIRFirestoreSourceDefault;
|
||||
}
|
||||
[_query getDocumentsWithSource:source completion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||
} else {
|
||||
NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot];
|
||||
resolve(data);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
source = FIRFirestoreSourceDefault;
|
||||
}
|
||||
[_query getDocumentsWithSource:source completion:^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
|
||||
if (error) {
|
||||
[RNFirebaseFirestore promiseRejectException:reject error:error];
|
||||
} else {
|
||||
NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot];
|
||||
resolve(data);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
+ (void)offSnapshot:(NSString *) listenerId {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
+ (void)offSnapshot:(NSString *)listenerId {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSnapshot:(NSString *) listenerId
|
||||
queryListenOptions:(NSDictionary *) queryListenOptions {
|
||||
if (_listeners[listenerId] == nil) {
|
||||
id listenerBlock = ^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) {
|
||||
if (error) {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
[self handleQuerySnapshotError:listenerId error:error];
|
||||
} else {
|
||||
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
||||
}
|
||||
};
|
||||
|
||||
bool includeMetadataChanges;
|
||||
if (queryListenOptions && queryListenOptions[@"includeMetadataChanges"]) {
|
||||
includeMetadataChanges = true;
|
||||
} else {
|
||||
includeMetadataChanges = false;
|
||||
- (void)onSnapshot:(NSString *)listenerId
|
||||
queryListenOptions:(NSDictionary *)queryListenOptions {
|
||||
if (_listeners[listenerId] == nil) {
|
||||
id listenerBlock = ^(FIRQuerySnapshot *_Nullable snapshot, NSError *_Nullable error) {
|
||||
if (error) {
|
||||
id<FIRListenerRegistration> listener = _listeners[listenerId];
|
||||
if (listener) {
|
||||
[_listeners removeObjectForKey:listenerId];
|
||||
[listener remove];
|
||||
}
|
||||
[self handleQuerySnapshotError:listenerId error:error];
|
||||
} else {
|
||||
[self handleQuerySnapshotEvent:listenerId querySnapshot:snapshot];
|
||||
}
|
||||
};
|
||||
|
||||
id<FIRListenerRegistration> listener = [_query addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
|
||||
_listeners[listenerId] = listener;
|
||||
bool includeMetadataChanges;
|
||||
if (queryListenOptions && queryListenOptions[@"includeMetadataChanges"]) {
|
||||
includeMetadataChanges = true;
|
||||
} else {
|
||||
includeMetadataChanges = false;
|
||||
}
|
||||
|
||||
id<FIRListenerRegistration>
|
||||
listener = [_query addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
|
||||
_listeners[listenerId] = listener;
|
||||
}
|
||||
}
|
||||
|
||||
- (FIRQuery *)buildQuery {
|
||||
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
|
||||
FIRQuery *query = (FIRQuery*)[firestore collectionWithPath:_path];
|
||||
query = [self applyFilters:firestore query:query];
|
||||
query = [self applyOrders:query];
|
||||
query = [self applyOptions:firestore query:query];
|
||||
FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:_appDisplayName];
|
||||
FIRQuery *query = (FIRQuery *) [firestore collectionWithPath:_path];
|
||||
query = [self applyFilters:firestore query:query];
|
||||
query = [self applyOrders:query];
|
||||
query = [self applyOptions:firestore query:query];
|
||||
|
||||
return query;
|
||||
return query;
|
||||
}
|
||||
|
||||
- (FIRQuery *)applyFilters:(FIRFirestore *) firestore
|
||||
query:(FIRQuery *) query {
|
||||
for (NSDictionary *filter in _filters) {
|
||||
NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
|
||||
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
||||
NSString *operator = filter[@"operator"];
|
||||
NSDictionary *jsValue = filter[@"value"];
|
||||
id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue];
|
||||
- (FIRQuery *)applyFilters:(FIRFirestore *)firestore
|
||||
query:(FIRQuery *)query {
|
||||
for (NSDictionary *filter in _filters) {
|
||||
NSDictionary *fieldPathDictionary = filter[@"fieldPath"];
|
||||
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
||||
NSString *operator = filter[@"operator"];
|
||||
NSDictionary *jsValue = filter[@"value"];
|
||||
id value = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:jsValue];
|
||||
|
||||
if ([fieldPathType isEqualToString:@"string"]) {
|
||||
NSString *fieldPath = fieldPathDictionary[@"string"];
|
||||
if ([operator isEqualToString:@"EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
||||
query = [query queryWhereField:fieldPath isGreaterThan:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
||||
query = [query queryWhereField:fieldPath isLessThan:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
|
||||
}
|
||||
} else {
|
||||
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
||||
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
||||
if ([operator isEqualToString:@"EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isGreaterThan:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isLessThan:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value];
|
||||
}
|
||||
}
|
||||
if ([fieldPathType isEqualToString:@"string"]) {
|
||||
NSString *fieldPath = fieldPathDictionary[@"string"];
|
||||
if ([operator isEqualToString:@"EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
||||
query = [query queryWhereField:fieldPath isGreaterThan:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
||||
query = [query queryWhereField:fieldPath isLessThan:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"ARRAY_CONTAINS"]) {
|
||||
query = [query queryWhereField:fieldPath arrayContains:value];
|
||||
}
|
||||
} else {
|
||||
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
||||
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
||||
if ([operator isEqualToString:@"EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isGreaterThan:value];
|
||||
} else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isGreaterThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isLessThan:value];
|
||||
} else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath isLessThanOrEqualTo:value];
|
||||
} else if ([operator isEqualToString:@"ARRAY_CONTAINS"]) {
|
||||
query = [query queryWhereFieldPath:fieldPath arrayContains:value];
|
||||
}
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
- (FIRQuery *)applyOrders:(FIRQuery *) query {
|
||||
for (NSDictionary *order in _orders) {
|
||||
NSString *direction = order[@"direction"];
|
||||
NSDictionary *fieldPathDictionary = order[@"fieldPath"];
|
||||
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
||||
- (FIRQuery *)applyOrders:(FIRQuery *)query {
|
||||
for (NSDictionary *order in _orders) {
|
||||
NSString *direction = order[@"direction"];
|
||||
NSDictionary *fieldPathDictionary = order[@"fieldPath"];
|
||||
NSString *fieldPathType = fieldPathDictionary[@"type"];
|
||||
|
||||
if ([fieldPathType isEqualToString:@"string"]) {
|
||||
NSString *fieldPath = fieldPathDictionary[@"string"];
|
||||
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
||||
} else {
|
||||
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
||||
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
||||
query = [query queryOrderedByFieldPath:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
||||
}
|
||||
if ([fieldPathType isEqualToString:@"string"]) {
|
||||
NSString *fieldPath = fieldPathDictionary[@"string"];
|
||||
query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
||||
} else {
|
||||
NSArray *fieldPathElements = fieldPathDictionary[@"elements"];
|
||||
FIRFieldPath *fieldPath = [[FIRFieldPath alloc] initWithFields:fieldPathElements];
|
||||
query = [query queryOrderedByFieldPath:fieldPath descending:([direction isEqualToString:@"DESCENDING"])];
|
||||
}
|
||||
return query;
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
- (FIRQuery *)applyOptions:(FIRFirestore *) firestore
|
||||
query:(FIRQuery *) query {
|
||||
if (_options[@"endAt"]) {
|
||||
query = [query queryEndingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endAt"]]];
|
||||
}
|
||||
if (_options[@"endBefore"]) {
|
||||
query = [query queryEndingBeforeValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endBefore"]]];
|
||||
}
|
||||
if (_options[@"limit"]) {
|
||||
query = [query queryLimitedTo:[_options[@"limit"] intValue]];
|
||||
}
|
||||
if (_options[@"offset"]) {
|
||||
// iOS doesn't support offset
|
||||
}
|
||||
if (_options[@"selectFields"]) {
|
||||
// iOS doesn't support selectFields
|
||||
}
|
||||
if (_options[@"startAfter"]) {
|
||||
query = [query queryStartingAfterValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAfter"]]];
|
||||
}
|
||||
if (_options[@"startAt"]) {
|
||||
query = [query queryStartingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAt"]]];
|
||||
}
|
||||
return query;
|
||||
- (FIRQuery *)applyOptions:(FIRFirestore *)firestore
|
||||
query:(FIRQuery *)query {
|
||||
if (_options[@"endAt"]) {
|
||||
query =
|
||||
[query queryEndingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endAt"]]];
|
||||
}
|
||||
if (_options[@"endBefore"]) {
|
||||
query =
|
||||
[query queryEndingBeforeValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"endBefore"]]];
|
||||
}
|
||||
if (_options[@"limit"]) {
|
||||
query = [query queryLimitedTo:[_options[@"limit"] intValue]];
|
||||
}
|
||||
if (_options[@"offset"]) {
|
||||
// iOS doesn't support offset
|
||||
}
|
||||
if (_options[@"selectFields"]) {
|
||||
// iOS doesn't support selectFields
|
||||
}
|
||||
if (_options[@"startAfter"]) {
|
||||
query =
|
||||
[query queryStartingAfterValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAfter"]]];
|
||||
}
|
||||
if (_options[@"startAt"]) {
|
||||
query =
|
||||
[query queryStartingAtValues:[RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:_options[@"startAt"]]];
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
- (void)handleQuerySnapshotError:(NSString *)listenerId
|
||||
error:(NSError *)error {
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_appDisplayName forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_appDisplayName forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
- (void)handleQuerySnapshotEvent:(NSString *)listenerId
|
||||
querySnapshot:(FIRQuerySnapshot *)querySnapshot {
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_appDisplayName forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
|
||||
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
|
||||
[event setValue:_appDisplayName forKey:@"appName"];
|
||||
[event setValue:_path forKey:@"path"];
|
||||
[event setValue:listenerId forKey:@"listenerId"];
|
||||
[event setValue:[RNFirebaseFirestoreCollectionReference snapshotToDictionary:querySnapshot] forKey:@"querySnapshot"];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_COLLECTION_SYNC_EVENT body:event];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot {
|
||||
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
|
||||
[snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"];
|
||||
[snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"];
|
||||
if (querySnapshot.metadata) {
|
||||
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
|
||||
[metadata setValue:@(querySnapshot.metadata.fromCache) forKey:@"fromCache"];
|
||||
[metadata setValue:@(querySnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
|
||||
[snapshot setValue:metadata forKey:@"metadata"];
|
||||
}
|
||||
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
|
||||
[snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"];
|
||||
[snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"];
|
||||
if (querySnapshot.metadata) {
|
||||
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
|
||||
[metadata setValue:@(querySnapshot.metadata.fromCache) forKey:@"fromCache"];
|
||||
[metadata setValue:@(querySnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
|
||||
[snapshot setValue:metadata forKey:@"metadata"];
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *) documentChanges {
|
||||
NSMutableArray *changes = [[NSMutableArray alloc] init];
|
||||
for (FIRDocumentChange *change in documentChanges) {
|
||||
[changes addObject:[self documentChangeToDictionary:change]];
|
||||
}
|
||||
+ (NSArray *)documentChangesToArray:(NSArray<FIRDocumentChange *> *)documentChanges {
|
||||
NSMutableArray *changes = [[NSMutableArray alloc] init];
|
||||
for (FIRDocumentChange *change in documentChanges) {
|
||||
[changes addObject:[self documentChangeToDictionary:change]];
|
||||
}
|
||||
|
||||
return changes;
|
||||
return changes;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange {
|
||||
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
|
||||
[change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"];
|
||||
[change setValue:@(documentChange.newIndex) forKey:@"newIndex"];
|
||||
[change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"];
|
||||
NSMutableDictionary *change = [[NSMutableDictionary alloc] init];
|
||||
[change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"];
|
||||
[change setValue:@(documentChange.newIndex) forKey:@"newIndex"];
|
||||
[change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"];
|
||||
|
||||
if (documentChange.type == FIRDocumentChangeTypeAdded) {
|
||||
[change setValue:@"added" forKey:@"type"];
|
||||
} else if (documentChange.type == FIRDocumentChangeTypeRemoved) {
|
||||
[change setValue:@"removed" forKey:@"type"];
|
||||
} else if (documentChange.type == FIRDocumentChangeTypeModified) {
|
||||
[change setValue:@"modified" forKey:@"type"];
|
||||
}
|
||||
if (documentChange.type == FIRDocumentChangeTypeAdded) {
|
||||
[change setValue:@"added" forKey:@"type"];
|
||||
} else if (documentChange.type == FIRDocumentChangeTypeRemoved) {
|
||||
[change setValue:@"removed" forKey:@"type"];
|
||||
} else if (documentChange.type == FIRDocumentChangeTypeModified) {
|
||||
[change setValue:@"modified" forKey:@"type"];
|
||||
}
|
||||
|
||||
return change;
|
||||
return change;
|
||||
}
|
||||
|
||||
+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *) documentSnapshots {
|
||||
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
|
||||
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
|
||||
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];
|
||||
}
|
||||
+ (NSArray *)documentSnapshotsToArray:(NSArray<FIRDocumentSnapshot *> *)documentSnapshots {
|
||||
NSMutableArray *snapshots = [[NSMutableArray alloc] init];
|
||||
for (FIRDocumentSnapshot *snapshot in documentSnapshots) {
|
||||
[snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]];
|
||||
}
|
||||
|
||||
return snapshots;
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-firebase",
|
||||
"version": "5.0.0-rc2",
|
||||
"version": "5.0.0-rc3",
|
||||
"author": "Invertase <oss@invertase.io> (http://invertase.io)",
|
||||
"description": "A well tested, feature rich Firebase implementation for React Native, supporting iOS & Android. Individual module support for Admob, Analytics, Auth, Crash Reporting, Cloud Firestore, Database, Dynamic Links, Functions, Messaging (FCM), Remote Config, Storage and more.",
|
||||
"main": "dist/index.js",
|
||||
|
||||
2
src/index.d.ts
vendored
2
src/index.d.ts
vendored
@@ -2670,7 +2670,7 @@ declare module 'react-native-firebase' {
|
||||
}
|
||||
|
||||
type QueryDirection = 'asc' | 'ASC' | 'desc' | 'DESC';
|
||||
type QueryOperator = '=' | '==' | '>' | '>=' | '<' | '<=';
|
||||
type QueryOperator = '=' | '==' | '>' | '>=' | '<' | '<=' | 'array-contains';
|
||||
|
||||
interface TypeMap {
|
||||
type:
|
||||
|
||||
@@ -7,7 +7,6 @@ import FieldPath from './FieldPath';
|
||||
import QuerySnapshot from './QuerySnapshot';
|
||||
import { buildNativeArray, buildTypeMap } from './utils/serialize';
|
||||
import { getAppEventName, SharedEventEmitter } from '../../utils/events';
|
||||
import { getLogger } from '../../utils/log';
|
||||
import { firestoreAutoId, isFunction, isObject } from '../../utils';
|
||||
import { getNativeModule } from '../../utils/native';
|
||||
|
||||
@@ -34,6 +33,7 @@ const OPERATORS: { [QueryOperator]: string } = {
|
||||
'>=': 'GREATER_THAN_OR_EQUAL',
|
||||
'<': 'LESS_THAN',
|
||||
'<=': 'LESS_THAN_OR_EQUAL',
|
||||
'array-contains': 'ARRAY_CONTAINS',
|
||||
};
|
||||
|
||||
type NativeFieldPath = {|
|
||||
@@ -200,6 +200,7 @@ export default class Query {
|
||||
observerOrOnNextOrOnError?: Observer | ObserverOnNext | ObserverOnError,
|
||||
onError?: ObserverOnError
|
||||
) {
|
||||
// TODO refactor this 💩
|
||||
let observer: Observer;
|
||||
let metadataChanges = {};
|
||||
// Called with: onNext, ?onError
|
||||
@@ -309,14 +310,15 @@ export default class Query {
|
||||
};
|
||||
|
||||
// Listen to snapshot events
|
||||
SharedEventEmitter.addListener(
|
||||
const snapshotSubscription = SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
|
||||
// Listen for snapshot error events
|
||||
let errorSubscription;
|
||||
if (observer.error) {
|
||||
SharedEventEmitter.addListener(
|
||||
errorSubscription = SharedEventEmitter.addListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
|
||||
observer.error
|
||||
);
|
||||
@@ -332,8 +334,19 @@ export default class Query {
|
||||
metadataChanges
|
||||
);
|
||||
|
||||
// Return an unsubscribe method
|
||||
return this._offCollectionSnapshot.bind(this, listenerId, listener);
|
||||
// return an unsubscribe method
|
||||
return () => {
|
||||
snapshotSubscription.remove();
|
||||
if (errorSubscription) errorSubscription.remove();
|
||||
// cancel native listener
|
||||
getNativeModule(this._firestore).collectionOffSnapshot(
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
listenerId
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
orderBy(
|
||||
@@ -414,6 +427,7 @@ export default class Query {
|
||||
operator: OPERATORS[opStr],
|
||||
value: nativeValue,
|
||||
};
|
||||
|
||||
const combinedFilters = this._fieldFilters.concat(newFilter);
|
||||
return new Query(
|
||||
this.firestore,
|
||||
@@ -455,27 +469,4 @@ export default class Query {
|
||||
|
||||
return buildNativeArray(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove query snapshot listener
|
||||
* @param listener
|
||||
*/
|
||||
_offCollectionSnapshot(listenerId: string, listener: Function) {
|
||||
getLogger(this._firestore).info('Removing onQuerySnapshot listener');
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshot:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
SharedEventEmitter.removeListener(
|
||||
getAppEventName(this._firestore, `onQuerySnapshotError:${listenerId}`),
|
||||
listener
|
||||
);
|
||||
getNativeModule(this._firestore).collectionOffSnapshot(
|
||||
this._referencePath.relativeName,
|
||||
this._fieldFilters,
|
||||
this._fieldOrders,
|
||||
this._queryOptions,
|
||||
listenerId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,14 @@ export type MetadataChanges = {|
|
||||
|
||||
export type QueryDirection = 'DESC' | 'desc' | 'ASC' | 'asc';
|
||||
|
||||
export type QueryOperator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
||||
export type QueryOperator =
|
||||
| '<'
|
||||
| '<='
|
||||
| '='
|
||||
| '=='
|
||||
| '>'
|
||||
| '>='
|
||||
| 'array-contains';
|
||||
|
||||
export type GetOptions = {
|
||||
source: 'default' | 'server' | 'cache',
|
||||
|
||||
532
tests/e2e/firestore/collection/cursors.e2e.js
Normal file
532
tests/e2e/firestore/collection/cursors.e2e.js
Normal file
@@ -0,0 +1,532 @@
|
||||
const {
|
||||
COL_DOC_1,
|
||||
cleanCollection,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
} = TestHelpers.firestore;
|
||||
|
||||
describe('firestore()', () => {
|
||||
before(async () => {
|
||||
await cleanCollection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const collection = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
await Promise.all([
|
||||
collection.doc('col1').set({ ...COL_DOC_1(), foo: 'bar0' }),
|
||||
collection.doc('col2').set({
|
||||
...COL_DOC_1(),
|
||||
foo: 'bar1',
|
||||
daz: 234,
|
||||
object: { daz: 234 },
|
||||
timestamp: new jet.context.window.Date(2017, 2, 11, 10, 0, 0),
|
||||
}),
|
||||
collection.doc('col3').set({
|
||||
...COL_DOC_1(),
|
||||
foo: 'bar2',
|
||||
daz: 345,
|
||||
object: { daz: 345 },
|
||||
timestamp: new jet.context.window.Date(2017, 2, 12, 10, 0, 0),
|
||||
}),
|
||||
collection.doc('col4').set({
|
||||
...COL_DOC_1(),
|
||||
foo: 'bar3',
|
||||
daz: 456,
|
||||
object: { daz: 456 },
|
||||
timestamp: new jet.context.window.Date(2017, 2, 13, 10, 0, 0),
|
||||
}),
|
||||
collection.doc('col5').set({
|
||||
...COL_DOC_1(),
|
||||
foo: 'bar4',
|
||||
daz: 567,
|
||||
object: { daz: 567 },
|
||||
timestamp: new jet.context.window.Date(2017, 2, 14, 10, 0, 0),
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('CollectionReference', () => {
|
||||
describe('cursors', () => {
|
||||
describe('endAt', () => {
|
||||
it('handles dates', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('timestamp')
|
||||
.endAt(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles numbers', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('daz')
|
||||
.endAt(345)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles strings', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endAt('bar2')
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.get();
|
||||
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endAt(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('works with FieldPath', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('timestamp'))
|
||||
.endAt(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots with FieldPath', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('foo'))
|
||||
.get();
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endAt(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
345,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('endBefore', () => {
|
||||
it('handles dates', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('timestamp')
|
||||
.endBefore(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles numbers', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('daz')
|
||||
.endBefore(345)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles strings', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endBefore('bar2')
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.get();
|
||||
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endBefore(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('works with FieldPath', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('timestamp'))
|
||||
.endBefore(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots with FieldPath', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('foo'))
|
||||
.get();
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.endBefore(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
123,
|
||||
234,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('startAt', () => {
|
||||
it('handles dates', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('timestamp')
|
||||
.startAt(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles numbers', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('daz')
|
||||
.startAt(345)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles strings', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAt('bar2')
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.get();
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAt(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('works with FieldPath', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('timestamp'))
|
||||
.startAt(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots with FieldPath', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('foo'))
|
||||
.get();
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAt(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
345,
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('startAfter', () => {
|
||||
it('handles dates', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('timestamp')
|
||||
.startAfter(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles numbers', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('daz')
|
||||
.startAfter(345)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles strings', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAfter('bar2')
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshot', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.get();
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAfter(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('works with FieldPath', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('timestamp'))
|
||||
.startAfter(new jet.context.window.Date(2017, 2, 12, 10, 0, 0))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
}));
|
||||
|
||||
it('handles snapshots with FieldPath', async () => {
|
||||
const collectionSnapshot = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy(new firebase.firestore.FieldPath('foo'))
|
||||
.get();
|
||||
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.orderBy('foo')
|
||||
.startAfter(collectionSnapshot.docs[2])
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 2);
|
||||
should.deepEqual(querySnapshot.docs.map(doc => doc.data().daz), [
|
||||
456,
|
||||
567,
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('orderBy()', () => {
|
||||
it('errors if called after startAt', () => {
|
||||
(() => {
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.startAt({})
|
||||
.orderBy('test');
|
||||
}).should.throw(
|
||||
'Cannot specify an orderBy() constraint after calling startAt(), startAfter(), endBefore() or endAt().'
|
||||
);
|
||||
});
|
||||
|
||||
it('errors if called after startAfter', () => {
|
||||
(() => {
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.startAfter({})
|
||||
.orderBy('test');
|
||||
}).should.throw(
|
||||
'Cannot specify an orderBy() constraint after calling startAt(), startAfter(), endBefore() or endAt().'
|
||||
);
|
||||
});
|
||||
|
||||
it('errors if called after endBefore', () => {
|
||||
(() => {
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.endBefore({})
|
||||
.orderBy('test');
|
||||
}).should.throw(
|
||||
'Cannot specify an orderBy() constraint after calling startAt(), startAfter(), endBefore() or endAt().'
|
||||
);
|
||||
});
|
||||
|
||||
it('errors if called after endAt', () => {
|
||||
(() => {
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.endAt({})
|
||||
.orderBy('test');
|
||||
}).should.throw(
|
||||
'Cannot specify an orderBy() constraint after calling startAt(), startAfter(), endBefore() or endAt().'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
61
tests/e2e/firestore/collection/limit.e2e.js
Normal file
61
tests/e2e/firestore/collection/limit.e2e.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const {
|
||||
COL_DOC_1,
|
||||
cleanCollection,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
} = TestHelpers.firestore;
|
||||
|
||||
describe('firestore()', () => {
|
||||
before(async () => {
|
||||
await cleanCollection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const collection = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
await Promise.all([
|
||||
collection.doc('col1').set(COL_DOC_1()),
|
||||
collection.doc('col2').set({ ...COL_DOC_1(), daz: 2 }),
|
||||
collection.doc('col3').set({ ...COL_DOC_1(), daz: 3 }),
|
||||
collection.doc('col4').set({ ...COL_DOC_1(), daz: 4 }),
|
||||
collection.doc('col5').set({ ...COL_DOC_1(), daz: 5 }),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('CollectionReference', () => {
|
||||
describe('limit()', () => {
|
||||
it('correctly works with get()', async () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.limit(3)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 3);
|
||||
}));
|
||||
|
||||
it('correctly works with onSnapshot()', async () => {
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.limit(3);
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
unsubscribe = collectionRef.onSnapshot(snapshot => {
|
||||
callback(snapshot.size);
|
||||
resolve2();
|
||||
});
|
||||
});
|
||||
|
||||
// Assertions
|
||||
callback.should.be.calledWith(3);
|
||||
|
||||
// Tear down
|
||||
unsubscribe();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
520
tests/e2e/firestore/collection/snapshot.e2e.js
Normal file
520
tests/e2e/firestore/collection/snapshot.e2e.js
Normal file
@@ -0,0 +1,520 @@
|
||||
const {
|
||||
COL_DOC_1,
|
||||
DOC_2_PATH,
|
||||
COL_DOC_1_PATH,
|
||||
cleanCollection,
|
||||
resetTestCollectionDoc,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
} = TestHelpers.firestore;
|
||||
|
||||
describe('firestore()', () => {
|
||||
describe('CollectionReference', () => {
|
||||
describe('onSnapshot()', () => {
|
||||
beforeEach(async () => {
|
||||
await sleep(50);
|
||||
await cleanCollection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
await sleep(50);
|
||||
});
|
||||
|
||||
it('QuerySnapshot has correct properties', async () => {
|
||||
const collection = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const snapshot = await collection.get();
|
||||
snapshot.docChanges.should.be.an.Array();
|
||||
snapshot.empty.should.equal(true);
|
||||
snapshot.metadata.should.be.an.Object();
|
||||
snapshot.query.should.be.an.Object();
|
||||
});
|
||||
|
||||
it('DocumentChange has correct properties', async () => {
|
||||
const collection = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
await resetTestCollectionDoc();
|
||||
|
||||
// Test
|
||||
let changes;
|
||||
let unsubscribe;
|
||||
await new Promise(resolve => {
|
||||
unsubscribe = collection.onSnapshot(snapshot => {
|
||||
changes = snapshot.docChanges;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
// Assertions
|
||||
changes.should.be.a.Array();
|
||||
changes[0].doc.should.be.an.Object();
|
||||
changes[0].newIndex.should.be.a.Number();
|
||||
changes[0].oldIndex.should.be.a.Number();
|
||||
changes[0].type.should.be.a.String();
|
||||
|
||||
// Tear down
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it('calls callback with the initial data and then when document changes', async () => {
|
||||
const callback = sinon.spy();
|
||||
const collection = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const newDocValue = { ...COL_DOC_1(), foo: 'updated' };
|
||||
|
||||
// Test
|
||||
let unsubscribe;
|
||||
let resolved = false;
|
||||
await new Promise(resolve => {
|
||||
unsubscribe = collection.onSnapshot(snapshot => {
|
||||
if (snapshot && snapshot.docs.length) {
|
||||
callback(snapshot.docs[0].data());
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledOnce();
|
||||
|
||||
await firebase
|
||||
.firestore()
|
||||
.doc(COL_DOC_1_PATH)
|
||||
.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
// Assertions
|
||||
callback.should.be.calledTwice();
|
||||
callback.getCall(1).args[0].foo.should.equal('updated');
|
||||
|
||||
// Tear down
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
// crappy race condition somewhere =/ will come back to it later
|
||||
it('calls callback with the initial data and then when document is added', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const newDocValue = { foo: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
unsubscribe = collectionRef.onSnapshot(snapshot => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
|
||||
const docRef = firebase.firestore().doc(DOC_2_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
callback.should.be.calledWith(newDocValue);
|
||||
callback.should.be.calledThrice();
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("doesn't call callback when the ref is updated with the same value", async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
unsubscribe = collectionRef.onSnapshot(snapshot => {
|
||||
snapshot.forEach(doc => {
|
||||
callback(doc.data());
|
||||
});
|
||||
resolve2();
|
||||
});
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(colDoc);
|
||||
|
||||
await sleep(150);
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledOnce(); // Callback is not called again
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it('allows binding multiple callbacks to the same ref', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
// Setup
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
const newDocValue = { ...colDoc, foo: '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(colDoc);
|
||||
callbackA.should.be.calledOnce();
|
||||
|
||||
callbackB.should.be.calledWith(colDoc);
|
||||
callbackB.should.be.calledOnce();
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
callbackA.should.be.calledWith(newDocValue);
|
||||
callbackB.should.be.calledWith(newDocValue);
|
||||
|
||||
callbackA.should.be.calledTwice();
|
||||
callbackB.should.be.calledTwice();
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribeA();
|
||||
unsubscribeB();
|
||||
});
|
||||
|
||||
it('listener stops listening when unsubscribed', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
// Setup
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
const newDocValue = { ...colDoc, foo: '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(colDoc);
|
||||
callbackA.should.be.calledOnce();
|
||||
|
||||
callbackB.should.be.calledWith(colDoc);
|
||||
callbackB.should.be.calledOnce();
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
callbackA.should.be.calledWith(newDocValue);
|
||||
callbackB.should.be.calledWith(newDocValue);
|
||||
|
||||
callbackA.should.be.calledTwice();
|
||||
callbackB.should.be.calledTwice();
|
||||
|
||||
// Unsubscribe A
|
||||
|
||||
unsubscribeA();
|
||||
|
||||
await docRef.set(colDoc);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
callbackB.should.be.calledWith(colDoc);
|
||||
|
||||
callbackA.should.be.calledTwice();
|
||||
callbackB.should.be.calledThrice();
|
||||
|
||||
// Unsubscribe B
|
||||
|
||||
unsubscribeB();
|
||||
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
callbackA.should.be.calledTwice();
|
||||
callbackB.should.be.calledThrice();
|
||||
});
|
||||
|
||||
it('supports options and callback', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
const newDocValue = { ...colDoc, foo: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
unsubscribe = collectionRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
snapshot => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith(newDocValue);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it('supports observer', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
const newDocValue = { ...colDoc, foo: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
const observer = {
|
||||
next: snapshot => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
},
|
||||
};
|
||||
unsubscribe = collectionRef.onSnapshot(observer);
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith(newDocValue);
|
||||
callback.should.be.calledTwice();
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it('supports options and observer', async () => {
|
||||
const colDoc = await resetTestCollectionDoc();
|
||||
await sleep(50);
|
||||
|
||||
const collectionRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
const newDocValue = { ...colDoc, foo: 'updated' };
|
||||
|
||||
const callback = sinon.spy();
|
||||
|
||||
// Test
|
||||
|
||||
let unsubscribe;
|
||||
await new Promise(resolve2 => {
|
||||
const observer = {
|
||||
next: snapshot => {
|
||||
snapshot.forEach(doc => callback(doc.data()));
|
||||
resolve2();
|
||||
},
|
||||
error: () => {},
|
||||
};
|
||||
unsubscribe = collectionRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
observer
|
||||
);
|
||||
});
|
||||
|
||||
callback.should.be.calledWith(colDoc);
|
||||
|
||||
const docRef = firebase.firestore().doc(COL_DOC_1_PATH);
|
||||
await docRef.set(newDocValue);
|
||||
|
||||
await sleep(25);
|
||||
|
||||
// Assertions
|
||||
|
||||
callback.should.be.calledWith(newDocValue);
|
||||
|
||||
// Tear down
|
||||
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it('errors when invalid parameters supplied', async () => {
|
||||
const colRef = firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
|
||||
(() => {
|
||||
colRef.onSnapshot(() => {}, 'error');
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Second argument must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot({
|
||||
next: () => {},
|
||||
error: 'error',
|
||||
});
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Observer.error must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot({
|
||||
next: 'error',
|
||||
});
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Observer.next must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
() => {},
|
||||
'error'
|
||||
);
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Third argument must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
{
|
||||
next: () => {},
|
||||
error: 'error',
|
||||
}
|
||||
);
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Observer.error must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
{
|
||||
next: 'error',
|
||||
}
|
||||
);
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Observer.next must be a valid function.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot(
|
||||
{
|
||||
includeMetadataChanges: true,
|
||||
},
|
||||
'error'
|
||||
);
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Second argument must be a function or observer.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot({
|
||||
error: 'error',
|
||||
});
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: First argument must be a function, observer or options.'
|
||||
);
|
||||
(() => {
|
||||
colRef.onSnapshot();
|
||||
}).should.throw(
|
||||
'Query.onSnapshot failed: Called with invalid arguments.'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
171
tests/e2e/firestore/collection/where.e2e.js
Normal file
171
tests/e2e/firestore/collection/where.e2e.js
Normal file
@@ -0,0 +1,171 @@
|
||||
const {
|
||||
COL_DOC_1,
|
||||
COL_DOC_1_PATH,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
resetTestCollectionDoc,
|
||||
} = TestHelpers.firestore;
|
||||
|
||||
describe('firestore()', () => {
|
||||
describe('CollectionReference', () => {
|
||||
before(() => resetTestCollectionDoc(COL_DOC_1_PATH, COL_DOC_1()));
|
||||
describe('where()', () => {
|
||||
it('`array-contains` a string value', async () => {
|
||||
const found = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('arrString', 'array-contains', 'a')
|
||||
.get();
|
||||
|
||||
should.equal(found.size, 1);
|
||||
found.forEach(documentSnapshot => {
|
||||
should.deepEqual(
|
||||
documentSnapshot.data().arrString,
|
||||
jet.contextify(['a', 'b', 'c', 'd'])
|
||||
);
|
||||
});
|
||||
|
||||
const notFound = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('arrString', 'array-contains', 'f')
|
||||
.get();
|
||||
|
||||
should.equal(notFound.size, 0);
|
||||
});
|
||||
|
||||
it('`array-contains` a number value', async () => {
|
||||
const found = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('arrNumber', 'array-contains', 1)
|
||||
.get();
|
||||
|
||||
should.equal(found.size, 1);
|
||||
found.forEach(documentSnapshot => {
|
||||
should.deepEqual(
|
||||
documentSnapshot.data().arrNumber,
|
||||
jet.contextify([1, 2, 3, 4])
|
||||
);
|
||||
});
|
||||
|
||||
const notFound = await firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('arrNumber', 'array-contains', 5)
|
||||
.get();
|
||||
|
||||
should.equal(notFound.size, 0);
|
||||
});
|
||||
|
||||
// TODO: below tests should also check the inverse to ensure working as
|
||||
// TODO: currently there is only one document in the collection so might be false positives in future
|
||||
it('== boolean value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('baz', '==', true)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().baz, true);
|
||||
});
|
||||
}));
|
||||
|
||||
it('== string value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('foo', '==', 'bar')
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().foo, 'bar');
|
||||
});
|
||||
}));
|
||||
|
||||
it('== null value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('naz', '==', null)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().naz, null);
|
||||
});
|
||||
}));
|
||||
|
||||
it('== date value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('timestamp', '==', COL_DOC_1().timestamp)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
}));
|
||||
|
||||
it('== GeoPoint value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('geopoint', '==', COL_DOC_1().geopoint)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
}));
|
||||
|
||||
it('>= number value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('daz', '>=', 123)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().daz, 123);
|
||||
});
|
||||
}));
|
||||
|
||||
it('>= GeoPoint value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('geopoint', '>=', new firebase.firestore.GeoPoint(-1, -1))
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
}));
|
||||
|
||||
it('<= float value', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where('gaz', '<=', 12.1234666)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().gaz, 12.1234567);
|
||||
});
|
||||
}));
|
||||
|
||||
it('FieldPath', () =>
|
||||
firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.where(new firebase.firestore.FieldPath('baz'), '==', true)
|
||||
.get()
|
||||
.then(querySnapshot => {
|
||||
should.equal(querySnapshot.size, 1);
|
||||
querySnapshot.forEach(documentSnapshot => {
|
||||
should.equal(documentSnapshot.data().baz, true);
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ const {
|
||||
COL2_DOC_1,
|
||||
COL2_DOC_1_ID,
|
||||
COL2_DOC_1_PATH,
|
||||
TEST2_COLLECTION_NAME,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
resetTestCollectionDoc,
|
||||
} = TestHelpers.firestore;
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('firestore()', () => {
|
||||
describe('parent', () => {
|
||||
it('should return parent collection', () => {
|
||||
const document = test2DocRef(COL2_DOC_1_ID);
|
||||
document.parent.id.should.equal(TEST2_COLLECTION_NAME);
|
||||
document.parent.id.should.equal(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
const TEST_COLLECTION_NAME = 'tests';
|
||||
const TEST2_COLLECTION_NAME = 'tests2';
|
||||
const TEST_COLLECTION_NAME_DYNAMIC = `tests${Math.floor(Math.random() * 30) +
|
||||
1}`;
|
||||
// const TEST3_COLLECTION_NAME = 'tests3';
|
||||
|
||||
let shouldCleanup = false;
|
||||
@@ -11,33 +13,36 @@ module.exports = {
|
||||
await Promise.all([
|
||||
module.exports.cleanCollection(TEST_COLLECTION_NAME),
|
||||
module.exports.cleanCollection(TEST2_COLLECTION_NAME),
|
||||
module.exports.cleanCollection(TEST_COLLECTION_NAME_DYNAMIC),
|
||||
]);
|
||||
|
||||
// await module.exports.cleanCollection(`${TEST_COLLECTION_NAME}3`);
|
||||
// await module.exports.cleanCollection(`${TEST_COLLECTION_NAME}4`);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
TEST_COLLECTION_NAME,
|
||||
TEST2_COLLECTION_NAME,
|
||||
TEST_COLLECTION_NAME_DYNAMIC,
|
||||
// TEST3_COLLECTION_NAME,
|
||||
|
||||
DOC_1: { name: 'doc1' },
|
||||
DOC_1_PATH: `tests/doc1${testRunId}`,
|
||||
DOC_1_PATH: `${TEST_COLLECTION_NAME_DYNAMIC}/doc1${testRunId}`,
|
||||
|
||||
DOC_2: { name: 'doc2', title: 'Document 2' },
|
||||
DOC_2_PATH: `tests/doc2${testRunId}`,
|
||||
DOC_2_PATH: `${TEST_COLLECTION_NAME_DYNAMIC}/doc2${testRunId}`,
|
||||
|
||||
// needs to be a fn as firebase may not yet be available
|
||||
COL_DOC_1() {
|
||||
shouldCleanup = true;
|
||||
return {
|
||||
testRunId,
|
||||
baz: true,
|
||||
daz: 123,
|
||||
foo: 'bar',
|
||||
gaz: 12.1234567,
|
||||
geopoint: new firebase.firestore.GeoPoint(0, 0),
|
||||
naz: null,
|
||||
arrNumber: [1, 2, 3, 4],
|
||||
arrString: ['a', 'b', 'c', 'd'],
|
||||
object: {
|
||||
daz: 123,
|
||||
},
|
||||
@@ -55,6 +60,8 @@ module.exports = {
|
||||
gaz: 12.1234567,
|
||||
geopoint: new firebase.firestore.GeoPoint(0, 0),
|
||||
naz: null,
|
||||
arrNumber: [1, 2, 3, 4],
|
||||
arrString: ['a', 'b', 'c', 'd'],
|
||||
object: {
|
||||
daz: 123,
|
||||
},
|
||||
@@ -63,10 +70,10 @@ module.exports = {
|
||||
},
|
||||
|
||||
COL_DOC_1_ID: `col1${testRunId}`,
|
||||
COL_DOC_1_PATH: `${TEST_COLLECTION_NAME}/col1${testRunId}`,
|
||||
COL_DOC_1_PATH: `${TEST_COLLECTION_NAME_DYNAMIC}/col1${testRunId}`,
|
||||
|
||||
COL2_DOC_1_ID: `doc1${testRunId}`,
|
||||
COL2_DOC_1_PATH: `${TEST2_COLLECTION_NAME}/doc1${testRunId}`,
|
||||
COL2_DOC_1_PATH: `${TEST_COLLECTION_NAME_DYNAMIC}/doc1${testRunId}`,
|
||||
|
||||
/**
|
||||
* Removes all documents on the collection for the current testId or
|
||||
@@ -76,7 +83,7 @@ module.exports = {
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
async cleanCollection(collectionName) {
|
||||
const firestore = firebaseAdmin.firestore();
|
||||
const firestore = firebase.firestore();
|
||||
const collection = firestore.collection(
|
||||
collectionName || TEST_COLLECTION_NAME
|
||||
);
|
||||
@@ -92,7 +99,8 @@ module.exports = {
|
||||
|
||||
if (
|
||||
ref.path.includes(testRunId) ||
|
||||
new Date(docsToDelete[i].createTime) <= yesterday
|
||||
new Date(docsToDelete[i].createTime) <= yesterday ||
|
||||
collectionName === TEST_COLLECTION_NAME_DYNAMIC
|
||||
) {
|
||||
batch.delete(ref);
|
||||
}
|
||||
@@ -109,7 +117,7 @@ module.exports = {
|
||||
shouldCleanup = true;
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST_COLLECTION_NAME)
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.doc(
|
||||
docId.startsWith(testRunId) || docId.endsWith(testRunId)
|
||||
? docId
|
||||
@@ -121,7 +129,7 @@ module.exports = {
|
||||
shouldCleanup = true;
|
||||
return firebase
|
||||
.firestore()
|
||||
.collection(TEST2_COLLECTION_NAME)
|
||||
.collection(TEST_COLLECTION_NAME_DYNAMIC)
|
||||
.doc(
|
||||
docId.startsWith(testRunId) || docId.endsWith(testRunId)
|
||||
? docId
|
||||
@@ -147,7 +155,7 @@ module.exports = {
|
||||
async resetTestCollectionDoc(path, doc) {
|
||||
shouldCleanup = true;
|
||||
const _doc = doc || module.exports.COL_DOC_1();
|
||||
|
||||
await module.exports.cleanCollection(TEST_COLLECTION_NAME_DYNAMIC);
|
||||
await firebase
|
||||
.firestore()
|
||||
.doc(path || module.exports.COL_DOC_1_PATH)
|
||||
|
||||
@@ -236,7 +236,7 @@ PODS:
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNFirebase (5.0.0-rc2):
|
||||
- RNFirebase (5.0.0-rc3):
|
||||
- Firebase/Core
|
||||
- React
|
||||
- yoga (0.57.0.React)
|
||||
|
||||
Reference in New Issue
Block a user