Firebase stub: refactoring

This commit is contained in:
gianantoniopini
2020-12-23 14:15:57 +01:00
parent f73359f83a
commit 356ae08643
2 changed files with 164 additions and 181 deletions

View File

@ -1,9 +1,9 @@
import FirebaseMock from '../gatsby-plugin-firebase_2'; import FirebaseStub from '../gatsby-plugin-firebase_2';
describe('database', () => { describe('database', () => {
it('reuses existing Database instance', () => { it('reuses existing Database instance', () => {
const database1 = FirebaseMock.database(); const database1 = FirebaseStub.database();
const database2 = FirebaseMock.database(); const database2 = FirebaseStub.database();
expect(database1.uuid).toBeTruthy(); expect(database1.uuid).toBeTruthy();
expect(database2.uuid).toBeTruthy(); expect(database2.uuid).toBeTruthy();
@ -11,8 +11,8 @@ describe('database', () => {
}); });
it('reuses existing Reference instance', () => { it('reuses existing Reference instance', () => {
const ref1 = FirebaseMock.database().ref('resumes/123'); const ref1 = FirebaseStub.database().ref('resumes/123');
const ref2 = FirebaseMock.database().ref('resumes/123'); const ref2 = FirebaseStub.database().ref('resumes/123');
expect(ref1.uuid).toBeTruthy(); expect(ref1.uuid).toBeTruthy();
expect(ref2.uuid).toBeTruthy(); expect(ref2.uuid).toBeTruthy();
@ -21,7 +21,7 @@ describe('database', () => {
it('ServerValue.TIMESTAMP returns current time in milliseconds', () => { it('ServerValue.TIMESTAMP returns current time in milliseconds', () => {
const now = new Date().getTime(); const now = new Date().getTime();
const timestamp = FirebaseMock.database.ServerValue.TIMESTAMP; const timestamp = FirebaseStub.database.ServerValue.TIMESTAMP;
expect(timestamp).toBeTruthy(); expect(timestamp).toBeTruthy();
expect(timestamp).toBeGreaterThanOrEqual(now); expect(timestamp).toBeGreaterThanOrEqual(now);
@ -30,12 +30,12 @@ describe('database', () => {
it("can spy on Reference 'update' function", async () => { it("can spy on Reference 'update' function", async () => {
const referencePath = 'resumes/123456'; const referencePath = 'resumes/123456';
const functionSpy = jest.spyOn( const functionSpy = jest.spyOn(
FirebaseMock.database().ref(referencePath), FirebaseStub.database().ref(referencePath),
'update', 'update',
); );
const updateArgument = 'test value 123'; const updateArgument = 'test value 123';
await FirebaseMock.database().ref(referencePath).update(updateArgument); await FirebaseStub.database().ref(referencePath).update(updateArgument);
expect(functionSpy).toHaveBeenCalledTimes(1); expect(functionSpy).toHaveBeenCalledTimes(1);
const functionCallArgument = functionSpy.mock.calls[0][0]; const functionCallArgument = functionSpy.mock.calls[0][0];
@ -44,73 +44,73 @@ describe('database', () => {
}); });
it('initializing data sets up resumes and users', async () => { it('initializing data sets up resumes and users', async () => {
FirebaseMock.database().initializeData(); FirebaseStub.database().initializeData();
const resumesRef = FirebaseMock.database().ref('resumes'); const resumesRef = FirebaseStub.database().ref('resumes');
const resumesDataSnapshot = await resumesRef.once('value'); const resumesDataSnapshot = await resumesRef.once('value');
const resumes = resumesDataSnapshot.val(); const resumes = resumesDataSnapshot.val();
expect(resumes).toBeTruthy(); expect(resumes).toBeTruthy();
expect(Object.keys(resumes).length).toEqual(2); expect(Object.keys(resumes).length).toEqual(2);
const demoResume = resumes[FirebaseMock.database().demoResumeId]; const demoResume = resumes[FirebaseStub.database().demoResumeId];
expect(demoResume).toBeTruthy(); expect(demoResume).toBeTruthy();
expect(demoResume.id).toEqual(FirebaseMock.database().demoResumeId); expect(demoResume.id).toEqual(FirebaseStub.database().demoResumeId);
const emptyResume = resumes[FirebaseMock.database().emptyResumeId]; const emptyResume = resumes[FirebaseStub.database().emptyResumeId];
expect(emptyResume).toBeTruthy(); expect(emptyResume).toBeTruthy();
expect(emptyResume.id).toEqual(FirebaseMock.database().emptyResumeId); expect(emptyResume.id).toEqual(FirebaseStub.database().emptyResumeId);
const usersRef = FirebaseMock.database().ref('users'); const usersRef = FirebaseStub.database().ref('users');
const usersDataSnapshot = await usersRef.once('value'); const usersDataSnapshot = await usersRef.once('value');
const users = usersDataSnapshot.val(); const users = usersDataSnapshot.val();
expect(users).toBeTruthy(); expect(users).toBeTruthy();
expect(Object.keys(users).length).toEqual(1); expect(Object.keys(users).length).toEqual(1);
const testUser = users[FirebaseMock.database().testUser.uid]; const testUser = users[FirebaseStub.database().testUser.uid];
expect(testUser).toBeTruthy(); expect(testUser).toBeTruthy();
expect(testUser.uid).toEqual(FirebaseMock.database().testUser.uid); expect(testUser.uid).toEqual(FirebaseStub.database().testUser.uid);
}); });
it('retrieves resume if it exists', async () => { it('retrieves resume if it exists', async () => {
FirebaseMock.database().initializeData(); FirebaseStub.database().initializeData();
const resume = ( const resume = (
await FirebaseMock.database() await FirebaseStub.database()
.ref(`resumes/${FirebaseMock.database().demoResumeId}`) .ref(`resumes/${FirebaseStub.database().demoResumeId}`)
.once('value') .once('value')
).val(); ).val();
expect(resume).toBeTruthy(); expect(resume).toBeTruthy();
expect(resume.id).toEqual(FirebaseMock.database().demoResumeId); expect(resume.id).toEqual(FirebaseStub.database().demoResumeId);
}); });
it('retrieves null if resume does not exist', async () => { it('retrieves null if resume does not exist', async () => {
FirebaseMock.database().initializeData(); FirebaseStub.database().initializeData();
const resumeId = 'invalidResumeId'; const resumeId = 'invalidResumeId';
const resume = ( const resume = (
await FirebaseMock.database().ref(`resumes/${resumeId}`).once('value') await FirebaseStub.database().ref(`resumes/${resumeId}`).once('value')
).val(); ).val();
expect(resume).toBeNull(); expect(resume).toBeNull();
}); });
it('retrieves user if it exists', async () => { it('retrieves user if it exists', async () => {
FirebaseMock.database().initializeData(); FirebaseStub.database().initializeData();
const user = ( const user = (
await FirebaseMock.database() await FirebaseStub.database()
.ref(`users/${FirebaseMock.database().testUser.uid}`) .ref(`users/${FirebaseStub.database().testUser.uid}`)
.once('value') .once('value')
).val(); ).val();
expect(user).toBeTruthy(); expect(user).toBeTruthy();
expect(user.uid).toEqual(FirebaseMock.database().testUser.uid); expect(user.uid).toEqual(FirebaseStub.database().testUser.uid);
}); });
it('retrieves null if user does not exist', async () => { it('retrieves null if user does not exist', async () => {
FirebaseMock.database().initializeData(); FirebaseStub.database().initializeData();
const userId = 'invalidUserId'; const userId = 'invalidUserId';
const user = ( const user = (
await FirebaseMock.database().ref(`users/${userId}`).once('value') await FirebaseStub.database().ref(`users/${userId}`).once('value')
).val(); ).val();
expect(user).toBeNull(); expect(user).toBeNull();
@ -119,12 +119,12 @@ describe('database', () => {
describe('auth', () => { describe('auth', () => {
beforeEach(() => { beforeEach(() => {
FirebaseMock.auth().clearOnAuthStateChangedObservers(); FirebaseStub.auth().clearOnAuthStateChangedObservers();
}); });
it('reuses existing Auth instance', () => { it('reuses existing Auth instance', () => {
const auth1 = FirebaseMock.auth(); const auth1 = FirebaseStub.auth();
const auth2 = FirebaseMock.auth(); const auth2 = FirebaseStub.auth();
expect(auth1.uuid).toBeTruthy(); expect(auth1.uuid).toBeTruthy();
expect(auth2.uuid).toBeTruthy(); expect(auth2.uuid).toBeTruthy();
@ -132,16 +132,16 @@ describe('auth', () => {
}); });
it('returns test user when signing in anonymously', async () => { it('returns test user when signing in anonymously', async () => {
const user = await FirebaseMock.auth().signInAnonymously(); const user = await FirebaseStub.auth().signInAnonymously();
expect(user).toBeTruthy(); expect(user).toBeTruthy();
expect(user).toEqual(FirebaseMock.database().testUser); expect(user).toEqual(FirebaseStub.database().testUser);
}); });
it('calls onAuthStateChanged observer with test user when signing in anonymously', async () => { it('calls onAuthStateChanged observer with test user when signing in anonymously', async () => {
let user = null; let user = null;
let error = null; let error = null;
FirebaseMock.auth().onAuthStateChanged( FirebaseStub.auth().onAuthStateChanged(
(_user) => { (_user) => {
user = _user; user = _user;
}, },
@ -150,24 +150,24 @@ describe('auth', () => {
}, },
); );
await FirebaseMock.auth().signInAnonymously(); await FirebaseStub.auth().signInAnonymously();
expect(user).toBeTruthy(); expect(user).toBeTruthy();
expect(user).toEqual(FirebaseMock.database().testUser); expect(user).toEqual(FirebaseStub.database().testUser);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
it('onAuthStateChanged unsubscribe removes observer', () => { it('onAuthStateChanged unsubscribe removes observer', () => {
const observer = () => {}; const observer = () => {};
const unsubscribe = FirebaseMock.auth().onAuthStateChanged(observer); const unsubscribe = FirebaseStub.auth().onAuthStateChanged(observer);
expect(unsubscribe).toBeTruthy(); expect(unsubscribe).toBeTruthy();
expect(FirebaseMock.auth().onAuthStateChangedObservers.length).toEqual(1); expect(FirebaseStub.auth().onAuthStateChangedObservers.length).toEqual(1);
expect(FirebaseMock.auth().onAuthStateChangedObservers[0]).toEqual( expect(FirebaseStub.auth().onAuthStateChangedObservers[0]).toEqual(
observer, observer,
); );
unsubscribe(); unsubscribe();
expect(FirebaseMock.auth().onAuthStateChangedObservers.length).toEqual(0); expect(FirebaseStub.auth().onAuthStateChangedObservers.length).toEqual(0);
}); });
}); });

View File

@ -48,38 +48,87 @@ class Auth {
} }
} }
class DataSnapshot { class Database {
#eventType = ''; static testUser = {
#reference = null; email: 'test.user@noemail.com',
name: 'Test User',
uid: 'testuser123',
};
static resumesPath = 'resumes';
static usersPath = 'users';
static #instance = undefined;
#uuid = '';
#data = {};
#references = {};
constructor(eventType, reference) { constructor() {
if (!eventType) { if (Database.#instance) {
throw new Error('eventType must be provided.'); return Database.#instance;
} else if (typeof eventType !== 'string') {
throw new Error('eventType should be a string.');
} }
this.#eventType = eventType; Database.#instance = this;
if (!reference) { this.#uuid = uuidv4();
throw new Error('reference must be provided.');
} else if (!(reference instanceof Reference)) {
throw new Error('reference must be an instance of the Reference class.');
}
this.#reference = reference;
} }
get eventType() { get testUser() {
return this.#eventType; return Database.testUser;
} }
val() { get demoResumeId() {
if (this.eventType === 'value') { return 'demore';
return this.#reference.getData(); }
get emptyResumeId() {
return 'mtre01';
}
get uuid() {
return this.#uuid;
}
static readFile(fileRelativePath) {
const fileAbsolutePath = path.resolve(__dirname, fileRelativePath);
const fileBuffer = fs.readFileSync(fileAbsolutePath);
const fileData = JSON.parse(fileBuffer);
return fileData;
}
initializeData() {
const resumes = {};
const demoResume = Database.readFile('../src/data/demoState.json');
resumes[this.demoResumeId] = demoResume;
const emptyResume = Database.readFile('../src/data/initialState.json');
resumes[this.emptyResumeId] = emptyResume;
for (var key in resumes) {
const resume = resumes[key];
resume.id = key;
resume.name = `Test Resume ${key}`;
resume.user = this.testUser.uid;
let date = new Date('December 15, 2020 11:20:25');
resume.updatedAt = date.valueOf();
date.setMonth(date.getMonth() - 2);
resume.createdAt = date.valueOf();
} }
return undefined; this.#data[Database.resumesPath] = resumes;
const users = {};
users[this.testUser.uid] = this.testUser;
this.#data[Database.usersPath] = users;
}
ref(path) {
const newRef = new Reference(path, () => this.#data);
const existingRef = this.#references[newRef.path];
if (existingRef) {
return existingRef;
}
this.#references[newRef.path] = newRef;
return newRef;
} }
} }
@ -170,136 +219,70 @@ class Reference {
async update(value) { async update(value) {
return Promise.resolve(true); return Promise.resolve(true);
} }
/*
const update = async (value) => {
if (resumesPath) {
if (value === null) {
delete __resumesDictionary[databaseLocationId];
} else {
__resumesDictionary[databaseLocationId] = value;
}
}
return Promise.resolve(true);
};
const set = (value) => {
if (resumesPath) {
if (value === null) {
delete __resumesDictionary[databaseLocationId];
} else {
__resumesDictionary[databaseLocationId] = value;
}
}
return Promise.resolve(true);
};
*/
} }
class Database { class DataSnapshot {
static testUser = { #eventType = '';
email: 'test.user@noemail.com', #reference = null;
name: 'Test User',
uid: 'testuser123',
};
static resumesPath = 'resumes';
static usersPath = 'users';
static #instance = undefined;
#uuid = '';
#data = {};
#references = {};
constructor() { constructor(eventType, reference) {
if (Database.#instance) { if (!eventType) {
return Database.#instance; throw new Error('eventType must be provided.');
} else if (typeof eventType !== 'string') {
throw new Error('eventType should be a string.');
} }
Database.#instance = this; this.#eventType = eventType;
this.#uuid = uuidv4(); if (!reference) {
} throw new Error('reference must be provided.');
} else if (!(reference instanceof Reference)) {
get testUser() { throw new Error('reference must be an instance of the Reference class.');
return Database.testUser;
}
get demoResumeId() {
return 'demore';
}
get emptyResumeId() {
return 'mtre01';
}
get uuid() {
return this.#uuid;
}
static readFile(fileRelativePath) {
const fileAbsolutePath = path.resolve(__dirname, fileRelativePath);
const fileBuffer = fs.readFileSync(fileAbsolutePath);
const fileData = JSON.parse(fileBuffer);
return fileData;
}
initializeData() {
const resumes = {};
const demoResume = Database.readFile('../src/data/demoState.json');
resumes[this.demoResumeId] = demoResume;
const emptyResume = Database.readFile('../src/data/initialState.json');
resumes[this.emptyResumeId] = emptyResume;
for (var key in resumes) {
const resume = resumes[key];
resume.id = key;
resume.name = `Test Resume ${key}`;
resume.user = this.testUser.uid;
let date = new Date('December 15, 2020 11:20:25');
resume.updatedAt = date.valueOf();
date.setMonth(date.getMonth() - 2);
resume.createdAt = date.valueOf();
} }
this.#data[Database.resumesPath] = resumes; this.#reference = reference;
const users = {};
users[this.testUser.uid] = this.testUser;
this.#data[Database.usersPath] = users;
} }
ref(path) { get eventType() {
const newRef = new Reference(path, () => this.#data); return this.#eventType;
const existingRef = this.#references[newRef.path]; }
if (existingRef) {
return existingRef; val() {
if (this.eventType === 'value') {
return this.#reference.getData();
} }
this.#references[newRef.path] = newRef; return undefined;
return newRef;
} }
} }
/* class FirebaseStub {
const database = () => {
const ref = (path) => {
const set = (value) => {
if (resumesPath) {
if (value === null) {
delete __resumesDictionary[databaseLocationId];
} else {
__resumesDictionary[databaseLocationId] = value;
}
}
return Promise.resolve(true);
};
const update = async (value) => {
if (resumesPath) {
if (value === null) {
delete __resumesDictionary[databaseLocationId];
} else {
__resumesDictionary[databaseLocationId] = value;
}
}
return Promise.resolve(true);
};
return {
once,
set,
update,
};
};
return {
__demoResumeId,
__emptyResumeId,
__init,
ref,
};
};
*/
class FirebaseMock {
static auth() { static auth() {
return new Auth(); return new Auth();
} }
@ -309,11 +292,11 @@ class FirebaseMock {
} }
} }
FirebaseMock.database.ServerValue = {}; FirebaseStub.database.ServerValue = {};
Object.defineProperty(FirebaseMock.database.ServerValue, 'TIMESTAMP', { Object.defineProperty(FirebaseStub.database.ServerValue, 'TIMESTAMP', {
get() { get() {
return new Date().getTime(); return new Date().getTime();
}, },
}); });
export default FirebaseMock; export default FirebaseStub;