Skip to content

Commit

Permalink
feat: add queryAsync method to Collection
Browse files Browse the repository at this point in the history
  • Loading branch information
thdk committed Dec 17, 2021
1 parent 2eeddbf commit a86bc63
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 27 deletions.
75 changes: 75 additions & 0 deletions src/collection/__tests__/query-async.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Collection, ICollectionOptions } from "../..";
import { logger } from "../../__test-utils__";


import { initializeTestEnvironment, RulesTestEnvironment } from "@firebase/rules-unit-testing";
import { collection, CollectionReference, limit } from "firebase/firestore";
const projectId = "test-query-async";

describe("Collection.queryAsync", () => {
let firestore: any;
let testEnv: RulesTestEnvironment;
let collectionRef: CollectionReference<any>;
let booksCollection: Collection<{ value: string }>;

function createCollection<T, K = T>(options?: ICollectionOptions<T, K>) {
return new Collection<T, K>(
firestore,
collectionRef,
options,
{
logger: logger
}
);
}

beforeAll(async () => {
testEnv = await initializeTestEnvironment({
projectId,
firestore: {
host: "localhost",
port: 8080,
}
});

firestore = testEnv.unauthenticatedContext().firestore();
collectionRef = collection(firestore, "books");
});


afterAll(() => testEnv.cleanup());

beforeEach(async () => {
await testEnv.clearFirestore()
booksCollection = createCollection();

return Promise.all(
[
booksCollection.addAsync({ value: "A" }),
booksCollection.addAsync({ value: "B" }),
],
);
});

afterEach(() => booksCollection.dispose());

test("it should return all docs when no constraints are given", async () => {
const books = await booksCollection.queryAsync();

expect(
books.length,
).toBe(2);

});

test("it should limit the docs when constraints are given", async () => {
const books = await booksCollection.queryAsync(
limit(1)
);

expect(
books.length,
).toBe(1);

});
});
59 changes: 32 additions & 27 deletions src/collection/__tests__/realtime-mode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Collection, ICollectionOptions, FetchMode, RealtimeMode } from "../..";
import { logger, addItemInBatch } from "../../__test-utils__";

import { CollectionReference, collection, writeBatch, setDoc, doc, addDoc, deleteDoc, query, where } from "firebase/firestore";
import { waitFor } from "@testing-library/dom";

describe("realtime mode", () => {
let booksCollection: Collection<{ value: string }>;
Expand Down Expand Up @@ -54,14 +55,15 @@ describe("realtime mode", () => {
afterEach(() => booksCollection.dispose());

describe("when documents are changed while watching the collection", () => {
test('it should update documents in local docs', () => {
return setDoc(doc(collectionRef, "id1"), {
test('it should update documents in local docs', async () => {
await setDoc(doc(collectionRef, "id1"), {
value: "B"
}).then(() => {
const doc = booksCollection.get("id1");
expect(doc).toBeDefined();
});

expect(doc!.data!.value).toBe("B");
const document = booksCollection.get("id1");
expect(document).toBeDefined();
await waitFor(() => {
expect(document!.data!.value).toBe("B");
});
});
});
Expand Down Expand Up @@ -113,45 +115,48 @@ describe("realtime mode", () => {

test('it should delete document in local docs', () => {
// Add one more document so we are not removing the last one (see next test)
return addDoc(collectionRef, { value: "A" }).then(() => {
return addDoc(collectionRef, { value: "A" }).then(async () => {
// Verify document to be deleted exists
const document = booksCollection.get("id1");
expect(document).toBeDefined();

return deleteDoc(doc(collectionRef, "id1"))
.then(() => {
// Verify if document has been deleted in local dictionary
const doc = booksCollection.get("id1");
expect(doc).toBeUndefined();
});
await deleteDoc(doc(collectionRef, "id1"));


await waitFor(() => {
// Verify if document has been deleted in local dictionary
const doc = booksCollection.get("id1");
expect(doc).toBeUndefined();
});
});
});

/** When deleting last document of collection, an emtpy snapshot is received instead of a snapshot with type = deleted */
test('it should delete document in local docs even if its the last one', () => {
test('it should delete document in local docs even if its the last one', async () => {
// Verify document to be deleted exists
const documemt = booksCollection.get("id1");
expect(documemt).toBeDefined();

return deleteDoc(doc(collectionRef, "id1"))
.then(() => {
// Verify if document has been deleted in local dictionary
const doc = booksCollection.get("id1");
expect(doc).toBeUndefined();
});
await deleteDoc(doc(collectionRef, "id1"));

await waitFor(() => {
// Verify if document has been deleted in local dictionary
const doc = booksCollection.get("id1");
expect(doc).toBeUndefined();
});
});
});

});

describe("when documents are added while watching the collection", () => {
test('it should add documents in local docs', () => {
return setDoc(doc(collectionRef, "id3"), { value: "C" })
.then(() => {
// Verify if document has been added to local dictionary
const doc = booksCollection.get("id3");
expect(doc).toBeDefined();
});
test('it should add documents in local docs', async () => {
await setDoc(doc(collectionRef, "id3"), { value: "C" });

await waitFor(() => {
const doc = booksCollection.get("id3");
expect(doc).toBeDefined();
});
});
});
});
Expand Down
23 changes: 23 additions & 0 deletions src/collection/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ import {
deleteDoc,
doc,
Firestore,
getDocs,
onSnapshot,
PartialWithFieldValue,
query as firestoryQuery,
Query,
QueryConstraint,
QuerySnapshot,
SetOptions,
updateDoc,
Expand Down Expand Up @@ -380,6 +383,26 @@ export class Collection<T, K = T> {
}));
}

/**
* Query the collection manually
* @param constraints
* @returns
*/
public queryAsync(...constraints: QueryConstraint[]) {
const q = firestoryQuery(this.collectionRef, ...constraints);

return getDocs(q)
.then((querySnapshot) => {
const docs: T[] = [];

querySnapshot.forEach((doc) => {
docs.push(this.deserialize(doc.data()));
});

return docs;
});
}

// TODO: when realtime updates is disabled, we must manually update the docs!
public deleteAsync(...ids: string[]) {
if (ids.length > 1) {
Expand Down

0 comments on commit a86bc63

Please sign in to comment.