Skip to content

Commit 82f1239

Browse files
author
William Sedlacek
authored
fix(firestore): make sure that #docChanges() emits right event type when list shifts (#19)
1 parent bcb6bdb commit 82f1239

2 files changed

Lines changed: 64 additions & 29 deletions

File tree

firestore/query-snapshot.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,33 @@ describe("#docChanges()", () => {
131131
});
132132
});
133133

134+
it("list docs as removed even when they are not the last in the list", () => {
135+
const docs = [
136+
new MockQueryDocumentSnapshot(query.firestore.doc("foo/0"), {}),
137+
new MockQueryDocumentSnapshot(query.firestore.doc("foo/1"), {}),
138+
new MockQueryDocumentSnapshot(query.firestore.doc("foo/2"), {}),
139+
new MockQueryDocumentSnapshot(query.firestore.doc("foo/3"), {}),
140+
];
141+
142+
createSnapshot(docs);
143+
const snapshot2 = createSnapshot([docs[0], docs[1], docs[3]]);
144+
145+
const changes = snapshot2.docChanges();
146+
expect(changes).toHaveLength(2);
147+
expect(changes).toContainEqual({
148+
type: "removed",
149+
oldIndex: 2,
150+
newIndex: -1,
151+
doc: docs[2],
152+
});
153+
expect(changes).toContainEqual({
154+
type: "modified",
155+
oldIndex: 3,
156+
newIndex: 2,
157+
doc: docs[3],
158+
});
159+
});
160+
134161
it("lists docs as added when they are not in the previous snapshot", async () => {
135162
const docs = [
136163
new MockQueryDocumentSnapshot(query.firestore.doc("foo/bar"), {}),
@@ -151,6 +178,25 @@ describe("#docChanges()", () => {
151178
});
152179
});
153180

181+
it("list changes that happen across different collections", () => {
182+
const docs = [
183+
new MockQueryDocumentSnapshot(query.firestore.doc("foo/a"), {}),
184+
new MockQueryDocumentSnapshot(query.firestore.doc("bar/a"), {}),
185+
];
186+
187+
createSnapshot(docs);
188+
const snapshot2 = createSnapshot([docs[0]]);
189+
190+
const changes = snapshot2.docChanges();
191+
expect(changes).toHaveLength(1);
192+
expect(changes).toContainEqual({
193+
type: "removed",
194+
oldIndex: 1,
195+
newIndex: -1,
196+
doc: docs[1],
197+
});
198+
});
199+
154200
describe("#forEach()", () => {
155201
it("iterates thru #docs", () => {
156202
const callback = jest.fn();

firestore/query-snapshot.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,54 +37,43 @@ export class MockQuerySnapshot<T = firebase.firestore.DocumentData>
3737
}));
3838
}
3939

40-
const iterationSize = Math.max(this.previousSnapshot.size, this.size);
4140
const checkedPaths = new Set<string>();
4241
const previousDocs = this.previousSnapshot.docs;
4342
const changes: firebase.firestore.DocumentChange<T>[] = [];
44-
for (let i = 0; i < iterationSize; i++) {
45-
const doc = this.docs[i] || previousDocs[i];
46-
if (checkedPaths.has(doc.ref.path)) {
47-
// This is a document which has a lower index in the current snapshot.
48-
// Skip it to avoid adding a modified change twice for it.
49-
continue;
50-
}
51-
52-
const newIndex =
53-
doc === this.docs[i]
54-
? i
55-
: this.docs.findIndex((another) => another.ref.path === doc.ref.path);
56-
const oldIndex =
57-
doc === previousDocs[i]
58-
? i
59-
: previousDocs.findIndex((another) => another.ref.path === doc.ref.path);
6043

61-
if (oldIndex === -1) {
62-
// Not in the old snapshot. Added.
44+
for (const [newIndex, doc] of this.docs.entries()) {
45+
const oldIndex = previousDocs.findIndex(({ ref }) => ref.path === doc.ref.path);
46+
const previousDoc = previousDocs[oldIndex];
47+
if (!previousDoc) {
6348
changes.push({
6449
type: "added",
6550
oldIndex,
6651
newIndex,
6752
doc,
6853
});
69-
} else if (newIndex === -1) {
70-
// Not in the new snapshot. Removed.
54+
} else if (newIndex !== oldIndex || !previousDoc.isEqual(doc)) {
7155
changes.push({
72-
type: "removed",
56+
type: "modified",
7357
oldIndex,
7458
newIndex,
7559
doc,
7660
});
77-
} else if (oldIndex !== newIndex || !previousDocs[oldIndex].isEqual(this.docs[newIndex])) {
78-
// Different index or not the same content anymore? It's a change.
61+
}
62+
63+
checkedPaths.add(doc.ref.path);
64+
}
65+
66+
for (const [oldIndex, doc] of previousDocs.entries()) {
67+
if (!checkedPaths.has(doc.ref.path)) {
7968
changes.push({
80-
type: "modified",
69+
type: "removed",
8170
oldIndex,
82-
newIndex,
83-
doc: this.docs[newIndex],
71+
newIndex: -1,
72+
doc,
8473
});
85-
}
8674

87-
checkedPaths.add(doc.ref.path);
75+
checkedPaths.add(doc.ref.path);
76+
}
8877
}
8978

9079
return changes;

0 commit comments

Comments
 (0)