From 45bf7146e5d82de349bef868b1e02212737d4a16 Mon Sep 17 00:00:00 2001 From: gianantoniopini <63844628+gianantoniopini@users.noreply.github.com> Date: Wed, 23 Dec 2020 13:07:15 +0100 Subject: [PATCH] Firebase mock: updated new implementation --- .../gatsby-plugin-firebase_2.test.js | 75 ++++++- __mocks__/gatsby-plugin-firebase_2.js | 209 +++++++++++------- 2 files changed, 203 insertions(+), 81 deletions(-) diff --git a/__mocks__/__tests__/gatsby-plugin-firebase_2.test.js b/__mocks__/__tests__/gatsby-plugin-firebase_2.test.js index febf78ad..1dcdef5e 100644 --- a/__mocks__/__tests__/gatsby-plugin-firebase_2.test.js +++ b/__mocks__/__tests__/gatsby-plugin-firebase_2.test.js @@ -1,7 +1,7 @@ import FirebaseMock from '../gatsby-plugin-firebase_2'; describe('database', () => { - it('reuses existing Database instance', async () => { + it('reuses existing Database instance', () => { const database1 = FirebaseMock.database(); const database2 = FirebaseMock.database(); @@ -10,7 +10,7 @@ describe('database', () => { expect(database1.uuid).toEqual(database2.uuid); }); - it('reuses existing Reference instance', async () => { + it('reuses existing Reference instance', () => { const ref1 = FirebaseMock.database().ref('resumes/123'); const ref2 = FirebaseMock.database().ref('resumes/123'); @@ -19,7 +19,7 @@ describe('database', () => { expect(ref1.uuid).toEqual(ref2.uuid); }); - it('ServerValue.TIMESTAMP returns current time in milliseconds', async () => { + it('ServerValue.TIMESTAMP returns current time in milliseconds', () => { const now = new Date().getTime(); const timestamp = FirebaseMock.database.ServerValue.TIMESTAMP; @@ -42,4 +42,73 @@ describe('database', () => { expect(functionCallArgument).toBeTruthy(); expect(functionCallArgument).toEqual(updateArgument); }); + + it('initializing data sets up resumes and users', async () => { + FirebaseMock.database().initializeData(); + + const resumesRef = FirebaseMock.database().ref('resumes'); + const resumesDataSnapshot = await resumesRef.once('value'); + const resumes = resumesDataSnapshot.val(); + expect(resumes).toBeTruthy(); + expect(Object.keys(resumes).length).toEqual(2); + const demoResume = resumes[FirebaseMock.database().demoResumeId]; + expect(demoResume).toBeTruthy(); + expect(demoResume.id).toEqual(FirebaseMock.database().demoResumeId); + const emptyResume = resumes[FirebaseMock.database().emptyResumeId]; + expect(emptyResume).toBeTruthy(); + expect(emptyResume.id).toEqual(FirebaseMock.database().emptyResumeId); + + const usersRef = FirebaseMock.database().ref('users'); + const usersDataSnapshot = await usersRef.once('value'); + const users = usersDataSnapshot.val(); + expect(users).toBeTruthy(); + expect(Object.keys(users).length).toEqual(1); + const testUser = users[FirebaseMock.database().testUser.uid]; + expect(testUser).toBeTruthy(); + expect(testUser.uid).toEqual(FirebaseMock.database().testUser.uid); + }); + + it('retrieves resume if it exists', async () => { + FirebaseMock.database().initializeData(); + + const resume = ( + await FirebaseMock.database() + .ref(`resumes/${FirebaseMock.database().demoResumeId}`) + .once('value') + ).val(); + expect(resume).toBeTruthy(); + expect(resume.id).toEqual(FirebaseMock.database().demoResumeId); + }); + + it('retrieves null if resume does not exist', async () => { + FirebaseMock.database().initializeData(); + const resumeId = 'invalidResumeId'; + + const resume = ( + await FirebaseMock.database().ref(`resumes/${resumeId}`).once('value') + ).val(); + expect(resume).toBeNull(); + }); + + it('retrieves user if it exists', async () => { + FirebaseMock.database().initializeData(); + + const user = ( + await FirebaseMock.database() + .ref(`users/${FirebaseMock.database().testUser.uid}`) + .once('value') + ).val(); + expect(user).toBeTruthy(); + expect(user.uid).toEqual(FirebaseMock.database().testUser.uid); + }); + + it('retrieves null if user does not exist', async () => { + FirebaseMock.database().initializeData(); + const userId = 'invalidUserId'; + + const user = ( + await FirebaseMock.database().ref(`users/${userId}`).once('value') + ).val(); + expect(user).toBeNull(); + }); }); diff --git a/__mocks__/gatsby-plugin-firebase_2.js b/__mocks__/gatsby-plugin-firebase_2.js index f2b565b8..fcd0f4ed 100644 --- a/__mocks__/gatsby-plugin-firebase_2.js +++ b/__mocks__/gatsby-plugin-firebase_2.js @@ -2,20 +2,64 @@ import path from 'path'; import fs from 'fs'; import { v4 as uuidv4 } from 'uuid'; -class Reference { - #path = ''; - #uuid = ''; +class DataSnapshot { + #eventType = ''; + #reference = null; - constructor(path) { - if (typeof path === 'undefined' || path === null) { - this.#path = '.'; - } else if (typeof path !== 'string') { - throw new Error('path should be a string.'); - } else { - this.#path = path; + constructor(eventType, reference) { + if (!eventType) { + throw new Error('eventType must be provided.'); + } else if (typeof eventType !== 'string') { + throw new Error('eventType should be a string.'); } + this.#eventType = eventType; + + if (!reference) { + 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() { + return this.#eventType; + } + + val() { + if (this.eventType === 'value') { + return this.#reference.getData(); + } + + return undefined; + } +} + +class Reference { + #rootPath = '.'; + #path = ''; + #uuid = ''; + #dataSnapshots = {}; + #getDatabaseData = () => null; + + constructor(path, getDatabaseData) { + if (typeof path === 'undefined' || path === null) { + this.#path = this.#rootPath; + } else if (typeof path !== 'string') { + throw new Error('path should be a string.'); + } + + if (!getDatabaseData) { + throw new Error('getDatabaseData must be provided.'); + } else if (typeof getDatabaseData !== 'function') { + throw new Error('getDatabaseData should be a function.'); + } + + this.#path = path; this.#uuid = uuidv4(); + this.#getDatabaseData = getDatabaseData; } get path() { @@ -26,17 +70,65 @@ class Reference { return this.#uuid; } + getData() { + const databaseData = this.#getDatabaseData(); + + if (!databaseData) { + return null; + } + + if (this.#path === this.#rootPath) { + return databaseData; + } + + if ( + this.#path === Database.resumesPath || + this.#path === Database.usersPath + ) { + return this.#path in databaseData ? databaseData[this.#path] : null; + } + + if ( + this.#path.startsWith(`${Database.resumesPath}/`) || + this.#path.startsWith(`${Database.usersPath}/`) + ) { + const databaseLocationId = this.#path.substring( + this.#path.indexOf('/') + 1, + ); + if (!databaseLocationId) { + throw new Error('Unknown database location id.'); + } + + const pathWithoutId = this.#path.substring(0, this.#path.indexOf('/')); + if (pathWithoutId in databaseData) { + return databaseLocationId in databaseData[pathWithoutId] + ? databaseData[pathWithoutId][databaseLocationId] + : null; + } + } + + return null; + } + + async once(eventType) { + const newDataSnapshot = new DataSnapshot(eventType, this); + const existingDataSnapshot = this.#dataSnapshots[newDataSnapshot.eventType]; + if (existingDataSnapshot) { + return Promise.resolve(existingDataSnapshot); + } + + this.#dataSnapshots[newDataSnapshot.eventType] = newDataSnapshot; + return Promise.resolve(newDataSnapshot); + } + async update(value) { return Promise.resolve(true); } } class Database { - static testUser = { - email: 'test.user@noemail.com', - name: 'Test User', - uid: 'testuser123', - }; + static resumesPath = 'resumes'; + static usersPath = 'users'; static #instance = undefined; #uuid = ''; #data = {}; @@ -52,6 +144,14 @@ class Database { this.#uuid = uuidv4(); } + get testUser() { + return { + email: 'test.user@noemail.com', + name: 'Test User', + uid: 'testuser123', + }; + } + get demoResumeId() { return 'demore'; } @@ -63,11 +163,18 @@ class Database { return this.#uuid; } - initData() { + 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 = readFile('../src/data/demoState.json'); + const demoResume = Database.readFile('../src/data/demoState.json'); resumes[this.demoResumeId] = demoResume; - const emptyResume = readFile('../src/data/initialState.json'); + const emptyResume = Database.readFile('../src/data/initialState.json'); resumes[this.emptyResumeId] = emptyResume; for (var key in resumes) { @@ -75,7 +182,7 @@ class Database { resume.id = key; resume.name = `Test Resume ${key}`; - resume.user = Database.testUser.uid; + resume.user = this.testUser.uid; let date = new Date('December 15, 2020 11:20:25'); resume.updatedAt = date.valueOf(); @@ -83,18 +190,15 @@ class Database { resume.createdAt = date.valueOf(); } - this.#data['resumes'] = resumes; - } + this.#data[Database.resumesPath] = resumes; - readFile(fileRelativePath) { - const fileAbsolutePath = path.resolve(__dirname, fileRelativePath); - const fileBuffer = fs.readFileSync(fileAbsolutePath); - const fileData = JSON.parse(fileBuffer); - return fileData; + const users = {}; + users[this.testUser.uid] = this.testUser; + this.#data[Database.usersPath] = users; } ref(path) { - const newRef = new Reference(path); + const newRef = new Reference(path, () => this.#data); const existingRef = this.#references[newRef.path]; if (existingRef) { return existingRef; @@ -108,47 +212,6 @@ class Database { /* const database = () => { const ref = (path) => { - if (!path) { - throw new Error('Not implemented.'); - } - - const resumesPath = path.startsWith('resumes/'); - const usersPath = path.startsWith('users/'); - if (!resumesPath && !usersPath) { - throw new Error('Unknown Reference path.'); - } - - const databaseLocationId = path.substring(path.indexOf('/') + 1); - if (!databaseLocationId) { - throw new Error('Unknown database location id.'); - } - - const once = async (eventType) => { - if (!eventType) { - throw new Error('Event type must be provided.'); - } - - if (eventType !== 'value') { - throw new Error('Unknown event type.'); - } - - const val = () => { - if (resumesPath) { - return __resumesDictionary[databaseLocationId] - ? __resumesDictionary[databaseLocationId] - : null; - } - - if (usersPath) { - return __testUser; - } - - return null; - }; - - return Promise.resolve({ val }); - }; - const set = (value) => { if (resumesPath) { if (value === null) { @@ -170,8 +233,6 @@ const database = () => { } } - __databaseRefUpdateCalls.push(value); - return Promise.resolve(true); }; @@ -179,7 +240,6 @@ const database = () => { once, set, update, - __updateCalls: __databaseRefUpdateCalls, }; }; @@ -190,13 +250,6 @@ const database = () => { ref, }; }; - -database.ServerValue = {}; -Object.defineProperty(database.ServerValue, 'TIMESTAMP', { - get() { - return new Date().getTime(); - }, -}); */ class FirebaseMock {