Skip to content

Commit

Permalink
add basic test and travis (#2)
Browse files Browse the repository at this point in the history
* add basic test and travis

* we sould actually test...

* add tests around single and multi records

* make commit a promise

* move mockFirebase function to util

* move mock to index file

* add test examples from firestore documentation

* add update example
  • Loading branch information
Scott Batson authored Oct 31, 2019
1 parent 2ef32de commit 1a37280
Show file tree
Hide file tree
Showing 9 changed files with 2,634 additions and 110 deletions.
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
sudo: false
language: node_js
node_js:
- '10'
cache:
directories:
- node_modules
before_install:
- npm update
install:
- npm install
script:
- npm test
102 changes: 102 additions & 0 deletions __tests__/full-setup.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const { mockFirebase } = require('firestore-jest-mock');
const { mockInitializeApp } = require('../mocks/firebase');

const {
mockAdd,
mockSet,
mockUpdate
} = require('../mocks/firestore');

let firebase;

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 }
],
cities: [
{ id: 'LA', name: 'Los Angeles', state: 'CA', country: 'USA' },
{ id: 'DC', name: 'Disctric of Columbia', state: 'DC', country: 'USA' }
]
}
});

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

test('We can start an application', async () => {
firebase.firestore();
expect(mockInitializeApp).toHaveBeenCalled();
});

describe('Examples from documentation', () => {
test('add a user', () => {
const db = firebase.firestore();

// 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');
});
});

test('get all users', () => {
const db = 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);

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\

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");

// Set the "capital" field of the city 'DC'
return washingtonRef.update({
capital: true
}).then(function () {
expect(mockUpdate).toHaveBeenCalledWith({ capital: true });
});
})
});
});
68 changes: 68 additions & 0 deletions __tests__/mock-firestore.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const { FakeFirestore } = require('firestore-jest-mock');

describe('Single records versus queries', () => {
const db = new FakeFirestore({
characters: [
{ id: 'homer', name: 'Homer', occupation: 'technician' },
{ id: 'krusty', name: 'Krusty', occupation: 'clown' }
]
});

test('it can fetch a single record', async () => {
const record = await db.collection('characters').doc('krusty').get();
expect(record.exists).toBe(true);
expect(record.id).toBe('krusty');
const data = record.data();
expect(data).toHaveProperty('name', 'Krusty');
expect(data).toHaveProperty('occupation', 'clown');
});

test('it flags records do not exist', async () => {
const record = await db.collection('animals').doc('monkey').get();
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(data).toHaveProperty('name', 'Homer');
expect(data).toHaveProperty('occupation', 'technician');
});
});

test('it can fetch multiple records and returns documents', async () => {
const records = await db.collection('characters').where('name', '==', 'Homer').get();

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 flags when a collection is empty', async () => {
const records = await db.collection('animals').where('type', '==', 'mammal').get();
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 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.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')
});
});
34 changes: 34 additions & 0 deletions __tests__/query.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const {
mockCollection,
mockGet,
mockWhere,
} = require('../mocks/firestore');

const { mockFirebase } = require('firestore-jest-mock');

describe('test', () => {
test('It can query firestore', async () => {
mockFirebase({
database: {
animals: [{ name: 'monkey', type: 'mammal' }, { name: 'elephant', type: 'mammal' }]
},
currentUser: { uid: 'homer-user' }
});
const firebase = require('firebase');
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});

const db = firebase.firestore();

const animals = await db.collection('animals')
.where('type', '==', 'mammal')
.get();

expect(mockWhere).toHaveBeenCalledWith('type', '==', 'mammal');
expect(mockCollection).toHaveBeenCalledWith('animals');
expect(mockGet).toHaveBeenCalled();
});
});
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const { FakeFirestore } = require('./mocks/firestore');
const { FakeAuth } = require('./mocks/auth');
const { mockFirebase } = require('./mocks/firebase');

module.exports = {
mockFirebase,
FakeFirestore,
FakeAuth
};
32 changes: 32 additions & 0 deletions mocks/firebase.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const mockInitializeApp = jest.fn();
const mockCert = jest.fn();

const firebaseStub = (overrides) => {
const { FakeFirestore, FakeAuth } = require('firestore-jest-mock');
return {
initializeApp: mockInitializeApp,

credential: {
cert: mockCert,
},

auth() {
return new FakeAuth(overrides.currentUser);
},

firestore() {
return new FakeFirestore(overrides.database);
}
}
};

const mockFirebase = (overrides= {}) => {
jest.mock('firebase', () => firebaseStub(overrides)) && jest.mock('firebase-admin', () => firebaseStub(overrides));
};

module.exports = {
firebaseStub,
mockFirebase,
mockInitializeApp,
mockCert
};
29 changes: 23 additions & 6 deletions mocks/firestore.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const mockCollection = jest.fn();
const mockDoc = jest.fn();
const mockWhere = jest.fn();
const mockBatch = jest.fn();
const mockGet = jest.fn();
const mockGetAll = jest.fn();
const mockUpdate = jest.fn();
const mockAdd = jest.fn();
Expand All @@ -25,6 +26,19 @@ function buildDocFromHash(hash = {}) {
};
}

function buildQuerySnapShot(requestedRecords) {
const multipleRecords = requestedRecords.filter(rec => !!rec);
const docs = multipleRecords.map(buildDocFromHash);

return {
empty: multipleRecords.length < 1,
docs,
forEach(callback) {
return docs.forEach(callback);
}
};
}

class FakeFirestore {
constructor(stubbedDatabase = {}) {
this.isFetchingSingle = false;
Expand All @@ -46,6 +60,8 @@ class FakeFirestore {
}

get() {
mockGet(...arguments);

if (this.recordToFetch) {
return Promise.resolve(buildDocFromHash(this.recordToFetch));
}
Expand All @@ -60,11 +76,7 @@ class FakeFirestore {
contentToReturn = buildDocFromHash(requestedRecords);
}
} else {
const multipleRecords = requestedRecords.filter(rec => !!rec);
contentToReturn = {
empty: multipleRecords.length < 1,
docs: multipleRecords.map(buildDocFromHash),
};
contentToReturn = buildQuerySnapShot(requestedRecords);
}

return Promise.resolve(contentToReturn);
Expand All @@ -79,7 +91,10 @@ class FakeFirestore {
.map(record => buildDocFromHash(record))
.filter(record => !!record.id);

return records;
return Promise.resolve({
empty: records.length < 1,
docs: records
});
}

batch() {
Expand All @@ -90,6 +105,7 @@ class FakeFirestore {
},
commit() {
mockBatchCommit(...arguments);
return Promise.resolve();
},
};
}
Expand Down Expand Up @@ -140,6 +156,7 @@ module.exports = {
mockCollection,
mockDelete,
mockDoc,
mockGet,
mockGetAll,
mockOrderBy,
mockSet,
Expand Down
Loading

0 comments on commit 1a37280

Please sign in to comment.