firestore-vitest
Version:
Vitest helper for mocking Google Cloud Firestore
377 lines (315 loc) • 12.4 kB
JavaScript
describe.each([
{ library: '@google-cloud/firestore', mockFunction: 'mockGoogleCloudFirestore' },
{ library: '@react-native-firebase/firestore', mockFunction: 'mockReactNativeFirestore' },
])('mocking $library with $mockFunction', ({ library, mockFunction }) => {
let mockGet;
let mockSelect;
let mockAdd;
let mockSet;
let mockUpdate;
let mockWhere;
let mockCollectionGroup;
let mockBatch;
let mockBatchCommit;
let mockBatchDelete;
let mockBatchUpdate;
let mockBatchSet;
let mockSettings;
let mockOnSnapShot;
let mockListCollections;
let mockTimestampNow;
let FirestoreMock;
let Timestamp;
const flushPromises = () => new Promise(setImmediate);
beforeAll(async () => {
FirestoreMock = await import('../index');
Timestamp = (await import('../mocks/timestamp')).Timestamp;
const fs = await import('../mocks/firestore');
mockGet = fs.mockGet;
mockSelect = fs.mockSelect;
mockAdd = fs.mockAdd;
mockSet = fs.mockSet;
mockUpdate = fs.mockUpdate;
mockWhere = fs.mockWhere;
mockCollectionGroup = fs.mockCollectionGroup;
mockBatch = fs.mockBatch;
mockBatchCommit = fs.mockBatchCommit;
mockBatchDelete = fs.mockBatchDelete;
mockBatchUpdate = fs.mockBatchUpdate;
mockBatchSet = fs.mockBatchSet;
mockSettings = fs.mockSettings;
mockOnSnapShot = fs.mockOnSnapShot;
mockListCollections = fs.mockListCollections;
mockTimestampNow = fs.mockTimestampNow;
});
describe('we can start a firestore application', () => {
beforeEach(async () => {
FirestoreMock[mockFunction]({
database: {
users: [
{ id: 'abc123', first: 'Bob', last: 'builder', born: 1998 },
{
id: '123abc',
first: 'Blues',
last: 'builder',
born: 1996,
_collections: {
cities: [
{ id: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA', visited: true },
],
},
},
],
cities: [
{ id: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA' },
{ id: 'DC', name: 'Disctric of Columbia', state: 'DC', country: 'USA' },
],
},
});
this.Firestore = (await import(library)).Firestore;
});
afterEach(() => mockTimestampNow.mockClear());
test('We can start an application', async () => {
const firestore = new this.Firestore();
firestore.settings({ ignoreUndefinedProperties: true });
expect(mockSettings).toHaveBeenCalledWith({ ignoreUndefinedProperties: true });
});
describe('Examples from documentation', () => {
test('add a user', () => {
const firestore = new this.Firestore();
return firestore
.collection('users')
.add({
first: 'Ada',
last: 'Lovelace',
born: 1815,
})
.then(function(docRef) {
expect(mockAdd).toHaveBeenCalled();
expect(docRef).toHaveProperty('id');
});
});
test('get all users', () => {
const firestore = new this.Firestore();
return firestore
.collection('users')
.get()
.then(querySnapshot => {
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(2);
expect(querySnapshot.size).toBe(querySnapshot.docs.length);
querySnapshot.forEach(doc => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
});
});
});
test('select specific fields only', () => {
const firestore = new this.Firestore();
return firestore
.collection('users')
.select('first', 'last')
.get()
.then(querySnapshot => {
expect(mockSelect).toHaveBeenCalledWith('first', 'last');
const data = querySnapshot.docs[0].data();
expect(Object.keys(data).length).toBe(2);
expect(data.first).toBe('Bob');
expect(data.last).toBe('builder');
});
});
test('select refs only', () => {
const firestore = new this.Firestore();
return firestore
.collection('users')
.select()
.get()
.then(querySnapshot => {
expect(mockSelect).toHaveBeenCalledWith();
const data = querySnapshot.docs[0].data();
expect(Object.keys(data).length).toBe(0);
});
});
test('collectionGroup at root', () => {
const firestore = new this.Firestore();
return firestore
.collectionGroup('users')
.where('last', '==', 'builder')
.get()
.then(querySnapshot => {
expect(mockCollectionGroup).toHaveBeenCalledWith('users');
expect(mockGet).toHaveBeenCalled();
expect(mockWhere).toHaveBeenCalledWith('last', '==', 'builder');
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(2);
expect(querySnapshot.size).toBe(querySnapshot.docs.length);
querySnapshot.forEach(doc => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
});
});
});
test('collectionGroup with subcollections', () => {
vi.clearAllMocks();
const firestore = new this.Firestore();
return firestore
.collectionGroup('cities')
.where('country', '==', 'USA')
.get()
.then(querySnapshot => {
expect(mockCollectionGroup).toHaveBeenCalledWith('cities');
expect(mockGet).toHaveBeenCalledTimes(1);
expect(mockWhere).toHaveBeenCalledWith('country', '==', 'USA');
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(3);
expect(querySnapshot.size).toBe(querySnapshot.docs.length);
querySnapshot.forEach(doc => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
});
});
});
test('set a city', () => {
const firestore = new this.Firestore();
return firestore
.collection('cities')
.doc('LA')
.set({
name: 'Los Angeles',
state: 'CA',
country: 'USA',
})
.then(function() {
expect(mockSet).toHaveBeenCalledWith({
name: 'Los Angeles',
state: 'CA',
country: 'USA',
});
});
});
test('updating a city', () => {
const firestore = new this.Firestore();
const now = Timestamp._fromMillis(new Date().getTime());
const washingtonRef = firestore.collection('cities').doc('DC');
mockTimestampNow.mockReturnValue(now);
return washingtonRef
.update({
capital: true,
})
.then(function(value) {
expect(value.updateTime).toStrictEqual(now);
expect(mockUpdate).toHaveBeenCalledWith({ capital: true });
});
});
test('batch writes', () => {
const firestore = new this.Firestore();
// Get a new write batch
const batch = firestore.batch();
// Set the value of 'NYC'
const nycRef = firestore.collection('cities').doc('NYC');
batch.set(nycRef, { name: 'New York City' });
// Update the population of 'SF'
const sfRef = firestore.collection('cities').doc('SF');
batch.update(sfRef, { population: 1000000 });
// Delete the city 'LA'
const laRef = firestore.collection('cities').doc('LA');
batch.delete(laRef);
// Commit the batch
return batch.commit().then(function() {
expect(mockBatch).toHaveBeenCalled();
expect(mockBatchDelete).toHaveBeenCalledWith(laRef);
expect(mockBatchUpdate).toHaveBeenCalledWith(sfRef, { population: 1000000 });
expect(mockBatchSet).toHaveBeenCalledWith(nycRef, { name: 'New York City' });
expect(mockBatchCommit).toHaveBeenCalled();
});
});
test('listCollections returns a promise', async () => {
const firestore = new this.Firestore();
const listCollectionsPromise = firestore.collection('cities').doc('LA').listCollections();
expect(listCollectionsPromise).toEqual(expect.any(Promise));
});
test('listCollections resolves with child collections', async () => {
const firestore = new this.Firestore();
const result = await firestore.collection('users').doc('123abc').listCollections();
expect(result).toEqual(expect.any(Array));
expect(result).toHaveLength(1);
expect(result[0]).toEqual(expect.any(this.Firestore.CollectionReference));
expect(result[0].id).toBe('cities');
});
test('listCollections resolves with empty array if there are no collections in document', async () => {
const firestore = new this.Firestore();
const result = await firestore.collection('users').doc('abc123').listCollections();
expect(result).toEqual(expect.any(Array));
expect(result).toHaveLength(0);
});
test('listCollections calls mockListCollections', async () => {
const firestore = new this.Firestore();
await firestore.collection('users').doc('abc123').listCollections();
expect(mockListCollections).toHaveBeenCalled();
});
test('onSnapshot single doc', async () => {
const firestore = new this.Firestore();
const now = Timestamp._fromMillis(new Date().getTime());
mockTimestampNow.mockReturnValue(now);
firestore
.collection('cities')
.doc('LA')
.onSnapshot(doc => {
expect(doc).toHaveProperty('createTime');
expect(doc).toHaveProperty('data');
expect(doc.data).toBeInstanceOf(Function);
expect(doc).toHaveProperty('metadata');
expect(doc).toHaveProperty('readTime');
expect(doc).toHaveProperty('updateTime');
expect(doc.readTime).toStrictEqual(now);
});
await flushPromises();
expect(mockOnSnapShot).toHaveBeenCalled();
});
test('onSnapshot can work with options', async () => {
const firestore = new this.Firestore();
const now = Timestamp._fromMillis(new Date().getTime());
mockTimestampNow.mockReturnValue(now);
firestore
.collection('cities')
.doc('LA')
.onSnapshot(
{
// Listen for document metadata changes
includeMetadataChanges: true,
},
doc => {
expect(doc).toHaveProperty('createTime');
expect(doc).toHaveProperty('data');
expect(doc.data).toBeInstanceOf(Function);
expect(doc).toHaveProperty('metadata');
expect(doc).toHaveProperty('readTime');
expect(doc).toHaveProperty('updateTime');
expect(doc.readTime).toStrictEqual(now);
},
);
await flushPromises();
expect(mockOnSnapShot).toHaveBeenCalled();
});
test('onSnapshot with query', async () => {
const firestore = new this.Firestore();
const unsubscribe = firestore
.collection('cities')
.where('state', '==', 'CA')
.onSnapshot(querySnapshot => {
expect(querySnapshot).toHaveProperty('forEach', expect.any(Function));
expect(querySnapshot).toHaveProperty('docChanges');
expect(querySnapshot).toHaveProperty('docs', expect.any(Array));
expect(querySnapshot.forEach).toBeInstanceOf(Function);
expect(querySnapshot.docChanges).toBeInstanceOf(Function);
expect(querySnapshot.docs).toBeInstanceOf(Array);
expect(querySnapshot.docChanges()).toBeInstanceOf(Array);
});
await flushPromises();
expect(unsubscribe).toBeInstanceOf(Function);
expect(mockWhere).toHaveBeenCalled();
expect(mockOnSnapShot).toHaveBeenCalled();
});
});
});
});