Skip to content

Commit 13bbf16

Browse files
Merge bf9b50c into 068edfa
2 parents 068edfa + bf9b50c commit 13bbf16

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

.changeset/calm-shrimps-press.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@firebase/firestore": patch
3+
---
4+
5+
Fixes an issue that returned invalid `DocumentReference`s in `QuerySnapshot`s.

packages/firestore/src/api/database.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -967,8 +967,11 @@ export class QueryDocumentSnapshot<T = PublicDocumentData>
967967
export class Query<T = PublicDocumentData>
968968
extends Compat<ExpQuery<T>>
969969
implements PublicQuery<T> {
970+
private readonly _userDataWriter: UserDataWriter;
971+
970972
constructor(readonly firestore: Firestore, delegate: ExpQuery<T>) {
971973
super(delegate);
974+
this._userDataWriter = new UserDataWriter(firestore);
972975
}
973976

974977
where(
@@ -1076,7 +1079,18 @@ export class Query<T = PublicDocumentData>
10761079
} else {
10771080
query = getDocs(this._delegate);
10781081
}
1079-
return query.then(result => new QuerySnapshot(this.firestore, result));
1082+
return query.then(
1083+
result =>
1084+
new QuerySnapshot(
1085+
this.firestore,
1086+
new ExpQuerySnapshot<T>(
1087+
this.firestore._delegate,
1088+
this._userDataWriter,
1089+
this._delegate,
1090+
result._snapshot
1091+
)
1092+
)
1093+
);
10801094
}
10811095

10821096
onSnapshot(observer: PartialObserver<PublicQuerySnapshot<T>>): Unsubscribe;
@@ -1100,7 +1114,16 @@ export class Query<T = PublicDocumentData>
11001114
const options = extractSnapshotOptions(args);
11011115
const observer = wrapObserver<QuerySnapshot<T>, ExpQuerySnapshot<T>>(
11021116
args,
1103-
snap => new QuerySnapshot(this.firestore, snap)
1117+
snap =>
1118+
new QuerySnapshot(
1119+
this.firestore,
1120+
new ExpQuerySnapshot<T>(
1121+
this.firestore._delegate,
1122+
this._userDataWriter,
1123+
this._delegate,
1124+
snap._snapshot
1125+
)
1126+
)
11041127
);
11051128
return onSnapshot(this._delegate, options, observer);
11061129
}

packages/firestore/test/integration/api/type.test.ts

+35-10
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { expect } from 'chai';
2020
import { addEqualityMatcher } from '../../util/equality_matcher';
2121
import * as firebaseExport from '../util/firebase_export';
2222
import { apiDescribe, withTestDb, withTestDoc } from '../util/helpers';
23+
import { EventsAccumulator } from '../util/events_accumulator';
2324

2425
const Blob = firebaseExport.Blob;
2526
const GeoPoint = firebaseExport.GeoPoint;
@@ -28,17 +29,33 @@ const Timestamp = firebaseExport.Timestamp;
2829
apiDescribe('Firestore', (persistence: boolean) => {
2930
addEqualityMatcher();
3031

31-
function expectRoundtrip(
32+
async function expectRoundtrip(
3233
db: firestore.FirebaseFirestore,
33-
data: {}
34+
data: {},
35+
validateSnapshots = true
3436
): Promise<void> {
35-
const doc = db.collection('rooms').doc();
36-
return doc
37-
.set(data)
38-
.then(() => doc.get())
39-
.then(snapshot => {
40-
expect(snapshot.data()).to.deep.equal(data);
41-
});
37+
const collection = db.collection(db.collection('a').doc().id);
38+
const doc = collection.doc();
39+
await doc.set(data);
40+
41+
let docSnapshot = await doc.get();
42+
expect(docSnapshot.data()).to.deep.equal(data);
43+
44+
if (validateSnapshots) {
45+
let querySnapshot = await collection.get();
46+
docSnapshot = querySnapshot.docs[0];
47+
expect(docSnapshot.data()).to.deep.equal(data);
48+
49+
const eventsAccumulator = new EventsAccumulator<
50+
firestore.QuerySnapshot
51+
>();
52+
const unlisten = collection.onSnapshot(eventsAccumulator.storeEvent);
53+
querySnapshot = await eventsAccumulator.awaitEvent();
54+
docSnapshot = querySnapshot.docs[0];
55+
expect(docSnapshot.data()).to.deep.equal(data);
56+
57+
unlisten();
58+
}
4259
}
4360

4461
it('can read and write null fields', () => {
@@ -49,7 +66,15 @@ apiDescribe('Firestore', (persistence: boolean) => {
4966

5067
it('can read and write number fields', () => {
5168
return withTestDb(persistence, db => {
52-
return expectRoundtrip(db, { a: 1, b: NaN, c: Infinity, d: -0.0 });
69+
// TODO(b/174486484): If we build ViewSnapshots from IndexedDb, this test
70+
// fails since we first store the backend proto in IndexedDb, which turns
71+
// -0.0 into 0.0.
72+
const validateSnapshots = !persistence;
73+
return expectRoundtrip(
74+
db,
75+
{ a: 1, b: NaN, c: Infinity, d: -0.0 },
76+
validateSnapshots
77+
);
5378
});
5479
});
5580

packages/firestore/test/util/equality_matcher.ts

+14
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ function customDeepEqual(
5757
return customMatcher.equalsFn(left, right);
5858
}
5959
}
60+
if (left && typeof left === 'object' && right && typeof right === 'object') {
61+
// The `isEqual` check below returns true if firestore-exp types are
62+
// compared with API types from Firestore classic. We do want to
63+
// differentiate between these types in our tests to ensure that the we do
64+
// not return firestore-exp types in the classic SDK.
65+
const leftObj = left as Record<string, unknown>;
66+
const rightObj = right as Record<string, unknown>;
67+
if (
68+
leftObj.constructor.name === rightObj.constructor.name &&
69+
leftObj.constructor !== rightObj.constructor
70+
) {
71+
return false;
72+
}
73+
}
6074
if (typeof left === 'object' && left && 'isEqual' in left) {
6175
return (left as Equatable<unknown>).isEqual(right);
6276
}

0 commit comments

Comments
 (0)