Skip to content

Commit

Permalink
add support for FieldPath on docRef.update
Browse files Browse the repository at this point in the history
  • Loading branch information
sydneyagcaoili authored and atn832 committed Feb 18, 2023
1 parent f1c2c06 commit e8db692
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 61 deletions.
28 changes: 21 additions & 7 deletions lib/src/mock_document_reference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,35 @@ class MockDocumentReference<T extends Object?> implements DocumentReference<T> {
message: 'Some requested document was not found.',
));
}
return _setRawData(Map.from(data));
return _setRawData(data);
}

/// Sets document raw data. Does not check for existence.
Future<void> _setRawData(Map<String, dynamic> data) async {
Future<void> _setRawData(Map<Object, Object?> data) async {
validateDocumentValue(data);
// Copy data so that subsequent change to `data` should not affect the data
// stored in mock document.
final copy = deepCopy(data);
copy.forEach((key, value) {
// document == root if key is not a composite key
final document = _findNestedDocumentToUpdate(key);
if (document != docsData[_path]) {
// Example, key: 'foo.bar.username', get 'username' field
key = key.split('.').last;
final Map<String, dynamic> document;
if (key is String) {
document = _findNestedDocumentToUpdate(key);
if (document != docsData[_path]) {
// Example, key: 'foo.bar.username', get 'username' field
key = key.split('.').last;
}
} else if (key is FieldPath) {
document = _findNestedDocumentToUpdate(key.components.join('.'));
if (document != docsData[_path]) {
// Example, key: FieldPath(['foo', 'bar', 'username']), get
// 'username' field
key = key.components.last;
}
} else {
throw ArgumentError('Key must be a String or FieldPath');
}

_applyValues(document, key, value);
});
_firestore.saveDocument(path);
Expand Down Expand Up @@ -239,7 +252,8 @@ class MockDocumentReference<T extends Object?> implements DocumentReference<T> {
_id,
docsData[_path],
convertedData,
/* converted */ true,
/* converted */
true,
exists,
options?.source == Source.cache,
);
Expand Down
5 changes: 4 additions & 1 deletion lib/src/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ void validateDocumentValue(dynamic value) {
validateDocumentValue(element);
}
return;
} else if (value is Map<String, dynamic>) {
} else if (value is Map) {
for (final element in value.values) {
validateDocumentValue(element);
}
Expand Down Expand Up @@ -125,3 +125,6 @@ dynamic transformDates(dynamic value) {
bool deepEqual(dynamic v1, dynamic v2) {
return DeepCollectionEquality().equals(v1, v2);
}

/// Returns a new [Iterable] with the elements of [list].
Iterable<R> toIterable<R>(List<R> list) => list.map((e) => e);
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ environment:
dependencies:
flutter:
sdk: flutter
cloud_firestore: ^4.0.0
cloud_firestore: ^4.4.0
cloud_firestore_platform_interface: ^5.0.1
collection: ^1.14.13
plugin_platform_interface: ^2.0.0
Expand Down
22 changes: 22 additions & 0 deletions test/fake_cloud_firestore_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,28 @@ void main() {
expect(document.get(FieldPath(['a', 'I.have.dots'])), 'c');
});

test('Update FieldPath array', () async {
final firestore = FakeFirebaseFirestore();

const id = 'someId';
await firestore.collection('users').doc(id).set({
'foo': {'bar': []}
});
await firestore.collection('users').doc(id).update({
FieldPath(['foo', 'bar']): FieldValue.arrayUnion(['baz']),
});
var snapshot = await firestore.collection('users').doc(id).get();
expect(snapshot, isNotNull);
expect(snapshot.data()?['foo']['bar'], ['baz']);

await firestore.collection('users').doc(id).update({
FieldPath(['foo', 'bar', 'baz']): {'qux': 'quux'},
});
snapshot = await firestore.collection('users').doc(id).get();
expect(snapshot, isNotNull);
expect(snapshot.data()?['foo']['bar']['baz'], {'qux': 'quux'});
});

test('Should throw StateError if field does not exist', () async {
final firestore = FakeFirebaseFirestore();
final collection = firestore.collection('test');
Expand Down
118 changes: 66 additions & 52 deletions test/mock_query_test.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fake_cloud_firestore/fake_cloud_firestore.dart';
import 'package:fake_cloud_firestore/src/util.dart';
import 'package:flutter/services.dart';
import 'package:test/test.dart';

Expand Down Expand Up @@ -453,23 +454,26 @@ void main() {
});
instance
.collection('posts')
.where('tags', arrayContainsAny: ['interesting', 'mostrecent'])
.where(
'tags',
arrayContainsAny: toIterable(['interesting', 'mostrecent']),
)
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(4));
}));
expect(snapshot.docs.length, equals(4));
}));
instance
.collection('posts')
.where('commenters', arrayContainsAny: [222, 333])
.where('commenters', arrayContainsAny: toIterable([222, 333]))
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(3));
}));
expect(snapshot.docs.length, equals(3));
}));
instance
.collection('posts')
.where(
'commenters',
arrayContainsAny: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
arrayContainsAny: toIterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]),
)
.snapshots()
.listen(null, onError: expectAsync1((error) {
Expand All @@ -496,34 +500,36 @@ void main() {
});
instance
.collection('contestants')
.where('country', whereIn: ['Japan', 'India'])
.where('country', whereIn: toIterable(['Japan', 'India']))
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(2));
}));
expect(snapshot.docs.length, equals(2));
}));
instance
.collection('contestants')
.where('country', whereIn: ['USA'])
.where('country', whereIn: toIterable(['USA']))
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(1));
}));
expect(snapshot.docs.length, equals(1));
}));
instance
.collection('contestants')
.where(
'country',
whereIn: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'],
whereIn: toIterable(
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'],
),
)
.snapshots()
.listen(null, onError: expectAsync1((error) {
expect(error, isA<ArgumentError>());
}));
expect(error, isA<ArgumentError>());
}));
instance
.collection('contestants')
.where(
'country',
whereIn: ['India'],
arrayContainsAny: ['USA'],
whereIn: toIterable(['India']),
arrayContainsAny: toIterable(['USA']),
)
.snapshots()
.listen(null, onError: expectAsync1((error) {
Expand All @@ -550,47 +556,49 @@ void main() {
});
instance
.collection('contestants')
.where('country', whereNotIn: ['Japan', 'India'])
.where('country', whereNotIn: toIterable(['Japan', 'India']))
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(1));
}));
expect(snapshot.docs.length, equals(1));
}));
instance
.collection('contestants')
.where('country', whereNotIn: ['USA'])
.where('country', whereNotIn: toIterable(['USA']))
.snapshots()
.listen(expectAsync1((QuerySnapshot snapshot) {
expect(snapshot.docs.length, equals(2));
}));
expect(snapshot.docs.length, equals(2));
}));
instance
.collection('contestants')
.where(
'country',
whereNotIn: [
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L'
],
whereNotIn: toIterable(
[
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
],
),
)
.snapshots()
.listen(null, onError: expectAsync1((error) {
expect(error, isA<ArgumentError>());
}));
expect(error, isA<ArgumentError>());
}));
instance
.collection('contestants')
.where(
'country',
whereNotIn: ['India'],
arrayContainsAny: ['USA'],
whereNotIn: toIterable(['India']),
arrayContainsAny: toIterable(['USA']),
)
.snapshots()
.listen(null, onError: expectAsync1((error) {
Expand Down Expand Up @@ -799,7 +807,7 @@ void main() {
final snapshots = await instance
.collection('messages')
.orderBy('Username')
.endBefore(['Bob']).get();
.endBefore(toIterable(['Bob'])).get();

expect(snapshots.docs, hasLength(1));
expect(
Expand Down Expand Up @@ -1374,10 +1382,10 @@ void main() {
final baseQuery =
instance.collection('cities').orderBy('name').orderBy('state');

var snapshots = await baseQuery.endAt(['Arizona']).get();
var snapshots = await baseQuery.endAt(toIterable(['Arizona'])).get();
expect(snapshots.docs.toData(), []);

snapshots = await baseQuery.endAt(['Springfield']).get();
snapshots = await baseQuery.endAt(toIterable(['Springfield'])).get();

expect(snapshots.docs.toData(), [
{
Expand Down Expand Up @@ -1408,7 +1416,7 @@ void main() {
},
]);

snapshots = await baseQuery.endAt(['Springfield', 'Missouri']).get();
snapshots = await baseQuery.endAt(toIterable(['Springfield', 'Missouri'])).get();

expect(snapshots.docs.toData(), [
{
Expand All @@ -1426,7 +1434,7 @@ void main() {
]);
// should get everything because wellington is alphabetically greater
// than every document in db
snapshots = await baseQuery.endAt(['Wellington']).get();
snapshots = await baseQuery.endAt(toIterable(['Wellington'])).get();
expect(snapshots.docs.toData(), [
{
'name': 'Los Angeles',
Expand Down Expand Up @@ -1534,20 +1542,26 @@ void main() {
});

test('arrayContainsAny', () async {
final querySnapshot =
await collection.where('dates', arrayContainsAny: [todayDate]).get();
final querySnapshot = await collection
.where('dates', arrayContainsAny: toIterable([todayDate]))
.get();
expect(querySnapshot.docs, isNotEmpty);
});

test('whereIn', () async {
final querySnapshot =
await collection.where('date', whereIn: [todayDate]).get();
final querySnapshot = await collection
.where('date', whereIn: toIterable([todayDate]))
.get();
expect(querySnapshot.docs, isNotEmpty);
});

test('whereNotIn', () async {
final querySnapshot = await collection
.where('date', whereNotIn: [todayDate.add(Duration(days: 1))]).get();
.where(
'date',
whereNotIn: toIterable([todayDate.add(Duration(days: 1))]),
)
.get();
expect(querySnapshot.docs, isNotEmpty);
});
});
Expand Down
13 changes: 13 additions & 0 deletions test/util_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,17 @@ void main() {
// result has only paths which contain "bar"
expect(result, root..remove('baz'));
});

test('toIterable', () {
final list = ['foo', 'bar', 'baz'];
expect(list, isA<Iterable>());
expect(list, isA<List>());


final result = toIterable(list);

// It should be an iterable
expect(result, isA<Iterable>());
expect(result, [...result]);
});
}

0 comments on commit e8db692

Please sign in to comment.