UNPKG

firestore-vitest

Version:

Vitest helper for mocking Google Cloud Firestore

488 lines (414 loc) 16.1 kB
describe.each` filters ${true} ${false} `('we can start a firebase application (query filters: $filters)', ({ filters }) => { // We call `require` inside of a parameterized `describe` so we get // a fresh mocked Firebase to test cases with query filters turned on and off vi.resetModules(); const flushPromises = () => new Promise(setImmediate); 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 mockUseEmulator; let mockDoc; let mockCollection; let mockWithConverter; let FakeFirestore; let mockQueryOnSnapshot; let mockTimestampNow; let mockFirebase; let mockInitializeApp; let Timestamp; let firebase; beforeAll(async () => { 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; mockUseEmulator = fs.mockUseEmulator; mockDoc = fs.mockDoc; mockCollection = fs.mockCollection; mockWithConverter = fs.mockWithConverter; FakeFirestore = fs.FakeFirestore; mockQueryOnSnapshot = fs.mockQueryOnSnapshot; mockTimestampNow = fs.mockTimestampNow; mockFirebase = (await import('../index')).mockFirebase; mockInitializeApp = (await import('../mocks/firebase')).mockInitializeApp; Timestamp = (await import('../mocks/timestamp')).Timestamp; mockFirebase( { 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 }, { id: 'Mex', name: 'Mexico City', country: 'Mexico', visited: true }, ], }, }, ], cities: [ { id: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA' }, { id: 'DC', name: 'Disctric of Columbia', state: 'DC', country: 'USA' }, ], }, }, { simulateQueryFilters: filters }, ); firebase = await import('firebase'); }); beforeEach(() => { vi.resetAllMocks(); firebase.initializeApp({ apiKey: '### FIREBASE API KEY ###', authDomain: '### FIREBASE AUTH DOMAIN ###', projectId: '### CLOUD FIRESTORE PROJECT ID ###', }); }); afterEach(() => mockTimestampNow.mockClear()); test('We can start an application', async () => { const db = firebase.firestore(); db.settings({ ignoreUndefinedProperties: true }); expect(mockInitializeApp).toHaveBeenCalled(); expect(mockSettings).toHaveBeenCalledWith({ ignoreUndefinedProperties: true }); }); test('we can use emulator', async () => { const db = firebase.firestore(); db.useEmulator('localhost', 9000); expect(mockUseEmulator).toHaveBeenCalledWith('localhost', 9000); }); describe('Examples from documentation', () => { test('add a user', () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/quickstart#add_data return db .collection('users') .add({ first: 'Ada', last: 'Lovelace', born: 1815, }) .then(function(docRef) { expect(mockAdd).toHaveBeenCalled(); expect(docRef).toHaveProperty('id'); expect(docRef).toHaveProperty('path', `users/${docRef.id}`); }); }); test('get all users', () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/quickstart#read_data return db .collection('users') .get() .then(querySnapshot => { expect(querySnapshot.forEach).toBeTruthy(); expect(querySnapshot.docs.length).toBe(2); expect(querySnapshot.size).toBe(querySnapshot.docs.length); const paths = querySnapshot.docs.map(d => d.ref.path).sort(); const expectedPaths = ['users/abc123', 'users/123abc'].sort(); expect(paths).toStrictEqual(expectedPaths); querySnapshot.forEach(doc => { expect(doc.exists).toBe(true); expect(doc.data()).toBeTruthy(); expect(doc.data().id).toBeFalsy(); }); }); }); test('select specific fields only', () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/quickstart#read_data return db .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 db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/quickstart#read_data return db .collection('users') .select() .get() .then(querySnapshot => { expect(mockSelect).toHaveBeenCalledWith(); const data = querySnapshot.docs[0].data(); expect(Object.keys(data).length).toBe(0); }); }); test('collectionGroup with collections only at root', () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/query-data/queries#collection-group-query return db .collectionGroup('users') .where('last', '==', 'builder') .get() .then(querySnapshot => { expect(mockCollectionGroup).toHaveBeenCalledWith('users'); expect(mockGet).toHaveBeenCalledTimes(1); 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', () => { const db = firebase.firestore(); return db .collectionGroup('cities') .get() .then(querySnapshot => { expect(mockCollectionGroup).toHaveBeenCalledWith('cities'); expect(mockGet).toHaveBeenCalledTimes(1); expect(mockWhere).not.toHaveBeenCalled(); expect(querySnapshot.forEach).toBeTruthy(); expect(querySnapshot.docs.length).toBe(4); expect(querySnapshot.size).toBe(querySnapshot.docs.length); querySnapshot.forEach(doc => { expect(doc.exists).toBe(true); expect(doc.data()).toBeTruthy(); }); }); }); test('collectionGroup with queried subcollections', () => { const db = firebase.firestore(); return db .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(filters ? 3 : 4); expect(querySnapshot.size).toBe(querySnapshot.docs.length); querySnapshot.forEach(doc => { expect(doc.exists).toBe(true); expect(doc.data()).toBeTruthy(); }); }); }); test('set a city', () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document\ return db .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 db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/manage-data/add-data#update-data const washingtonRef = db.collection('cities').doc('DC'); const now = Timestamp._fromMillis(new Date().getTime()); mockTimestampNow.mockReturnValue(now); // Set the "capital" field of the city 'DC' return washingtonRef .update({ capital: true, }) .then(function(value) { expect(value.updateTime).toStrictEqual(now); expect(mockUpdate).toHaveBeenCalledWith({ capital: true }); }); }); test('batch writes', () => { const db = firebase.firestore(); // Example from documentation: // https://cloud.google.com/firestore/docs/manage-data/transactions // Get a new write batch const batch = db.batch(); // Set the value of 'NYC' const nycRef = db.collection('cities').doc('NYC'); batch.set(nycRef, { name: 'New York City' }); // Update the population of 'SF' const sfRef = db.collection('cities').doc('SF'); batch.update(sfRef, { population: 1000000 }); // Delete the city 'LA' const laRef = db.collection('cities').doc('LA'); batch.delete(laRef); // Commit the batch return batch.commit().then(function(result) { expect(result).toBeInstanceOf(Array); 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 method does not exist', async () => { const db = firebase.firestore(); expect(() => { db.collection('cities').doc('LA').listCollections(); }).toThrow(TypeError); }); test('onSnapshot single doc', async () => { const db = firebase.firestore(); const now = Timestamp._fromMillis(new Date().getTime()); mockTimestampNow.mockReturnValue(now); // Example from documentation: // https://firebase.google.com/docs/firestore/query-data/listen db.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(); expect(mockQueryOnSnapshot).not.toHaveBeenCalled(); }); test('onSnapshot can work with options', async () => { const db = firebase.firestore(); const now = Timestamp._fromMillis(new Date().getTime()); mockTimestampNow.mockReturnValue(now); // Example from documentation: // https://firebase.google.com/docs/firestore/query-data/listen db.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(); expect(mockQueryOnSnapshot).not.toHaveBeenCalled(); }); test('onSnapshot with query', async () => { const db = firebase.firestore(); // Example from documentation: // https://firebase.google.com/docs/firestore/query-data/listen const unsubscribe = db .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).not.toHaveBeenCalled(); expect(mockQueryOnSnapshot).toHaveBeenCalled(); }); describe('withConverter', () => { const converter = { fromFirestore: () => {}, toFirestore: () => {}, }; test('single document', async () => { const db = firebase.firestore(); const recordDoc = db.doc('cities/la').withConverter(converter); expect(mockDoc).toHaveBeenCalledWith('cities/la'); expect(mockWithConverter).toHaveBeenCalledWith(converter); expect(recordDoc).toBeInstanceOf(FakeFirestore.DocumentReference); const record = await recordDoc.get(); expect(mockGet).toHaveBeenCalled(); expect(record).toHaveProperty('id', 'la'); expect(record.data).toBeInstanceOf(Function); }); test('single undefined document', async () => { const db = firebase.firestore(); const recordDoc = db.collection('cities').withConverter(converter).doc(); expect(mockCollection).toHaveBeenCalledWith('cities'); expect(mockWithConverter).toHaveBeenCalledWith(converter); expect(mockDoc).toHaveBeenCalled(); expect(recordDoc).toBeInstanceOf(FakeFirestore.DocumentReference); const record = await recordDoc.get(); expect(mockGet).toHaveBeenCalled(); expect(record).toHaveProperty('id'); expect(record.data).toBeInstanceOf(Function); }); test('multiple documents', async () => { const db = firebase.firestore(); const recordsCol = db.collection('cities').withConverter(converter); expect(mockCollection).toHaveBeenCalledWith('cities'); expect(mockWithConverter).toHaveBeenCalledWith(converter); expect(recordsCol).toBeInstanceOf(FakeFirestore.CollectionReference); const records = await recordsCol.get(); expect(mockGet).toHaveBeenCalled(); expect(records).toHaveProperty('docs', expect.any(Array)); }); }); }); });