Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand on our Timestamp mock #39

Merged
merged 17 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,25 @@ Your application doesn't double-check firestore's response -- it trusts that it'
| `mockIncrementFieldValue` | Assert a number field is incremented by the correct amount | [increment](https://googleapis.dev/nodejs/firestore/latest/FieldValue.html#.increment) |
| `mockServerTimestampFieldValue` | Assert a server Firebase.Timestamp value will be stored | [serverTimestamp](https://googleapis.dev/nodejs/firestore/latest/FieldValue.html#.serverTimestamp) |

#### [Firestore.Timestamp](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html)

| Method | Use | Method in Firestore |
| ------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `mockTimestampToDate` | Assert the call and mock the result, or use the default implementation | [toDate](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html#toDate) |
| `mockTimestampToMillis` | Assert the call and mock the result, or use the default implementation | [toMillis](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html#toMillis) |
| `mockTimestampFromDate` | Assert the call and mock the result, or use the default implementation | [fromDate](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html#.fromDate) |
| `mockTimestampFromMillis` | Assert the call and mock the result, or use the default implementation | [fromMillis](https://googleapis.dev/nodejs/firestore/latest/Timestamp.html#.fromMillis) |

#### [Firestore.Transaction](https://googleapis.dev/nodejs/firestore/latest/Transaction.html)

| Method | Use | Method in Firestore |
| ----------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `mockGetTransaction` | Assert transaction.get is called with correct params. Returns a promise | [get](https://googleapis.dev/nodejs/firestore/latest/Transaction.html#get) |
| `mockGetAllTransaction` | Assert transaction.getAll is called with correct params. Returns a promise | [get](https://googleapis.dev/nodejs/firestore/latest/Transaction.html#getAll) |
| `mockSetTransaction` | Assert transaction.set is called with correct params. Returns the transaction object | [set](https://googleapis.dev/nodejs/firestore/latest/Transaction.html#set) |
| `mockUpdateTransaction` | Assert transaction.update is called with correct params. Returns the transaction object | [update](https://googleapis.dev/nodejs/firestore/latest/Transaction.html#update) |
| `mockDeleteTransaction` | Assert transaction.delete is called with correct params. Returns the transaction object | [delete](https://googleapis.dev/nodejs/firestore/latest/Transaction.html#delete) |

#### [Auth](https://firebase.google.com/docs/reference/js/firebase.auth.Auth)

| Method | Use | Method in Firebase |
Expand Down
97 changes: 57 additions & 40 deletions __tests__/full-setup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@ const {
mockBatchCommit,
mockBatchDelete,
mockBatchUpdate,
mockBatchSet
mockBatchSet,
} = require('../mocks/firestore');

describe('we can start a firebase application', () => {
mockFirebase({
database: {
users: [
{ id: 'abc123', first: 'Bob', last: 'builder', born: 1998 },
{ id: '123abc', first: 'Blues', last: 'builder', born: 1996 }
{ id: '123abc', first: 'Blues', last: 'builder', born: 1996 },
],
cities: [
{ id: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA' },
{ id: 'DC', name: 'Disctric of Columbia', state: 'DC', country: 'USA' }
]
}
{ id: 'DC', name: 'Disctric of Columbia', state: 'DC', country: 'USA' },
],
},
});

beforeEach(() => {
this.firebase = require('firebase');
this.firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
projectId: '### CLOUD FIRESTORE PROJECT ID ###',
});
});

Expand All @@ -50,39 +50,46 @@ describe('we can start a firebase application', () => {
// Example from documentation:
// https://firebase.google.com/docs/firestore/quickstart#add_data

db.collection('users').add({
first: 'Ada',
last: 'Lovelace',
born: 1815
}).then(function (docRef) {
expect(mockAdd).toHaveBeenCalled();
expect(docRef).toHaveProperty('id', 'abc123');
expect(docRef.data()).toHaveProperty('first', 'Ada');
});
return db
.collection('users')
.add({
first: 'Ada',
last: 'Lovelace',
born: 1815,
})
.then(function(docRef) {
expect(mockAdd).toHaveBeenCalled();
expect(docRef).toHaveProperty('id', 'abc123');
expect(docRef.data()).toHaveProperty('first', 'Ada');
});
});

test('get all users', () => {
const db = this.firebase.firestore();
// Example from documentation:
// https://firebase.google.com/docs/firestore/quickstart#read_data

db.collection('users').get().then((querySnapshot) => {
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(2);
return db
.collection('users')
.get()
.then(querySnapshot => {
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(2);

querySnapshot.forEach((doc) => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
querySnapshot.forEach(doc => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
});
});
});
});

test('collectionGroup', () => {
const db = this.firebase.firestore();
// Example from documentation:
// https://firebase.google.com/docs/firestore/query-data/queries#collection-group-query

db.collectionGroup('cities')
return db
.collectionGroup('cities')
.where('type', '==', 'museum')
.get()
.then(querySnapshot => {
Expand All @@ -93,7 +100,7 @@ describe('we can start a firebase application', () => {
expect(querySnapshot.forEach).toBeTruthy();
expect(querySnapshot.docs.length).toBe(2);

querySnapshot.forEach((doc) => {
querySnapshot.forEach(doc => {
expect(doc.exists).toBe(true);
expect(doc.data()).toBeTruthy();
});
Expand All @@ -105,27 +112,37 @@ describe('we can start a firebase application', () => {
// Example from documentation:
// https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document\

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' });
});
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 = this.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 washingtonRef = db.collection('cities').doc('DC');

// Set the "capital" field of the city 'DC'
return washingtonRef.update({
capital: true
}).then(function () {
expect(mockUpdate).toHaveBeenCalledWith({ capital: true });
});
return washingtonRef
.update({
capital: true,
})
.then(function() {
expect(mockUpdate).toHaveBeenCalledWith({ capital: true });
});
});

test('batch writes', () => {
Expand All @@ -142,17 +159,17 @@ describe('we can start a firebase application', () => {

// Update the population of 'SF'
const sfRef = db.collection('cities').doc('SF');
batch.update(sfRef, { 'population': 1000000 });
batch.update(sfRef, { population: 1000000 });

// Delete the city 'LA'
const laRef = db.collection('cities').doc('LA');
batch.delete(laRef);

// Commit the batch
batch.commit().then(function () {
return batch.commit().then(function() {
expect(mockBatch).toHaveBeenCalled();
expect(mockBatchDelete).toHaveBeenCalledWith(laRef);
expect(mockBatchUpdate).toHaveBeenCalledWith(sfRef, { 'population': 1000000 });
expect(mockBatchUpdate).toHaveBeenCalledWith(sfRef, { population: 1000000 });
expect(mockBatchSet).toHaveBeenCalledWith(nycRef, { name: 'New York City' });
expect(mockBatchCommit).toHaveBeenCalled();
});
Expand Down
85 changes: 50 additions & 35 deletions __tests__/mock-firestore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ describe('Single records versus queries', () => {
expect(record.exists).toBe(true);
expect(record.id).toBe('krusty');
const data = record.data();
expect(record).toHaveProperty('exists', true);
expect(data).toBeDefined();
expect(data).toHaveProperty('name', 'Krusty');
expect(data).toHaveProperty('occupation', 'clown');
});
Expand All @@ -34,30 +36,36 @@ describe('Single records versus queries', () => {
expect(record.exists).toBe(false);
});

test('it can fetch a single record with a promise', () => db
.collection('characters')
.doc('homer')
.get()
.then(record => {
expect(record.exists).toBe(true);
expect(record.id).toBe('homer');
const data = record.data();
expect(mockCollection).toHaveBeenCalledWith('characters');
expect(data).toHaveProperty('name', 'Homer');
expect(data).toHaveProperty('occupation', 'technician');
}));
test('it can fetch a single record with a promise', () =>
db
.collection('characters')
.doc('homer')
.get()
.then(record => {
expect(record.exists).toBe(true);
expect(record.id).toBe('homer');
expect(mockCollection).toHaveBeenCalledWith('characters');
const data = record.data();
expect(record).toHaveProperty('exists', true);
expect(data).toBeDefined();
expect(data).toHaveProperty('name', 'Homer');
expect(data).toHaveProperty('occupation', 'technician');
}));

test('it can fetch a single record with a promise without a specified collection', () => db
.doc('characters/homer')
.get()
.then(record => {
expect(record.exists).toBe(true);
expect(record.id).toBe('homer');
const data = record.data();
expect(mockCollection).not.toHaveBeenCalled();
expect(data).toHaveProperty('name', 'Homer');
expect(data).toHaveProperty('occupation', 'technician');
}));
test('it can fetch a single record with a promise without a specified collection', () =>
db
.doc('characters/homer')
.get()
.then(record => {
expect(record.exists).toBe(true);
expect(record.id).toBe('homer');
expect(mockCollection).not.toHaveBeenCalled();
const data = record.data();
expect(record).toHaveProperty('exists', true);
expect(data).toBeDefined();
expect(data).toHaveProperty('name', 'Homer');
expect(data).toHaveProperty('occupation', 'technician');
}));

test('it can fetch multiple records and returns documents', async () => {
const records = await db
Expand All @@ -67,8 +75,12 @@ describe('Single records versus queries', () => {

expect(records.empty).toBe(false);
expect(records).toHaveProperty('docs', expect.any(Array));
expect(records.docs[0]).toHaveProperty('id', 'homer');
expect(records.docs[0].data()).toHaveProperty('name', 'Homer');
const doc = records.docs[0];
expect(doc).toHaveProperty('id', 'homer');
expect(doc).toHaveProperty('exists', true);
const data = doc.data();
expect(data).toBeDefined();
expect(data).toHaveProperty('name', 'Homer');
});

test('it flags when a collection is empty', async () => {
Expand All @@ -79,23 +91,26 @@ describe('Single records versus queries', () => {
expect(records.empty).toBe(true);
});

test('it can fetch multiple records as a promise', () => db
.collection('characters')
.where('name', '==', 'Homer')
.get()
.then(records => {
expect(records.empty).toBe(false);
expect(records).toHaveProperty('docs', expect.any(Array));
expect(records.docs[0]).toHaveProperty('id', 'homer');
expect(records.docs[0].data()).toHaveProperty('name', 'Homer');
}));
test('it can fetch multiple records as a promise', () =>
db
.collection('characters')
.where('name', '==', 'Homer')
.get()
.then(records => {
expect(records.empty).toBe(false);
expect(records).toHaveProperty('docs', expect.any(Array));
expect(records.docs[0]).toHaveProperty('id', 'homer');
expect(records.docs[0].data()).toHaveProperty('name', 'Homer');
}));

test('it can return all records', async () => {
const firstRecord = db.collection('characters').doc('homer');
const secondRecord = db.collection('characters').doc('krusty');

const records = await db.getAll(firstRecord, secondRecord);
expect(records[0]).toHaveProperty('id', 'homer');
expect(records[0]).toHaveProperty('exists', true);
expect(records[0].data()).toBeDefined();
expect(records[0].data()).toHaveProperty('name', 'Homer');
});
});
71 changes: 71 additions & 0 deletions __tests__/mock-timestamp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const { FakeFirestore } = require('firestore-jest-mock');
const {
mockTimestampToDate,
mockTimestampToMillis,
} = require('firestore-jest-mock/mocks/firestore');

describe('Timestamp mock', () => {
test('it is equal to itself', () => {
const timestamp = new FakeFirestore.Timestamp(500, 20);
expect(timestamp.isEqual(timestamp)).toBe(true);
});

test('it is equal to a separate instance', () => {
const timestamp = new FakeFirestore.Timestamp(500, 20);
const other = new FakeFirestore.Timestamp(500, 20);
expect(timestamp.isEqual(other)).toBe(true);
});

test('it is not equal to an instance whose properties differ', () => {
const timestamp = new FakeFirestore.Timestamp(500, 20);
const diffSeconds = new FakeFirestore.Timestamp(550, 20);
const diffNano = new FakeFirestore.Timestamp(500, 40);
const diffAll = new FakeFirestore.Timestamp(550, 40);
expect(timestamp.isEqual(diffSeconds)).toBe(false);
expect(timestamp.isEqual(diffNano)).toBe(false);
expect(timestamp.isEqual(diffAll)).toBe(false);
});

test('it converts itself roughly to a Date representation', () => {
// Since this is a mock (and I'm bad with time maths) we don't expect nanosecond accuracy
const timestamp = new FakeFirestore.Timestamp(40, 0);
expect(timestamp.toDate()).toBeInstanceOf(Date);
expect(timestamp.toDate().getSeconds()).toBe(40);
expect(mockTimestampToDate).toHaveBeenCalled();
});

test('it allows clients to override the Date representation', () => {
const timestamp = new FakeFirestore.Timestamp(40, 0);
const now = new Date();
mockTimestampToDate.mockReturnValueOnce(now);
expect(timestamp.toDate()).toBe(now);
expect(timestamp.toDate().getSeconds()).toBe(40); // second call should be the original
});

test('it converts itself roughly to millisecond representation', () => {
// The mock only returns 0, but it calls mockTimestampToMillis first, returning its result if defined
const timestamp = new FakeFirestore.Timestamp(40, 80);
const now = new Date();
mockTimestampToMillis.mockReturnValueOnce(now.getMilliseconds());
expect(timestamp.toMillis()).toBe(now.getMilliseconds());
expect(timestamp.toMillis()).toBe(0); // second call should be the original
expect(mockTimestampToMillis).toHaveBeenCalledTimes(2);
});

test('it creates an instance roughly from a Date representation', () => {
const now = new Date();
const timestamp = FakeFirestore.Timestamp.fromDate(now);
expect(timestamp).toBeDefined();
expect(timestamp).toBeInstanceOf(FakeFirestore.Timestamp);
expect(timestamp.toDate().getTime()).toBe(now.getTime());
});

test('it creates an instance roughly from a millisecond representation', () => {
const date = new Date(0);
date.setMilliseconds(54000);
const timestamp = FakeFirestore.Timestamp.fromMillis(54000);
expect(timestamp).toBeDefined();
expect(timestamp).toBeInstanceOf(FakeFirestore.Timestamp);
expect(timestamp.seconds).toBe(date.getSeconds());
});
});
Loading