-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1040 from JGreenlee/rewrite-services-sept2023
✏️ Rewrite KVStore, ClientStats, and CommHelper
- Loading branch information
Showing
36 changed files
with
770 additions
and
747 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import packageJsonBuild from '../../package.cordovabuild.json'; | ||
|
||
export const mockCordova = () => { | ||
window['cordova'] ||= {}; | ||
window['cordova'].platformId ||= 'ios'; | ||
window['cordova'].platformVersion ||= packageJsonBuild.dependencies['cordova-ios']; | ||
window['cordova'].plugins ||= {}; | ||
} | ||
|
||
export const mockDevice = () => { | ||
window['device'] ||= {}; | ||
window['device'].platform ||= 'ios'; | ||
window['device'].version ||= '14.0.0'; | ||
} | ||
|
||
export const mockGetAppVersion = () => { | ||
const mockGetAppVersion = { | ||
getAppName: () => new Promise((rs, rj) => setTimeout(() => rs('Mock App'), 10)), | ||
getPackageName: () => new Promise((rs, rj) => setTimeout(() => rs('com.example.mockapp'), 10)), | ||
getVersionCode: () => new Promise((rs, rj) => setTimeout(() => rs('123'), 10)), | ||
getVersionNumber: () => new Promise((rs, rj) => setTimeout(() => rs('1.2.3'), 10)), | ||
} | ||
window['cordova'] ||= {}; | ||
window['cordova'].getAppVersion = mockGetAppVersion; | ||
} | ||
|
||
export const mockBEMUserCache = () => { | ||
const _cache = {}; | ||
const messages = []; | ||
const mockBEMUserCache = { | ||
getLocalStorage: (key: string, isSecure: boolean) => { | ||
return new Promise((rs, rj) => | ||
setTimeout(() => { | ||
rs(_cache[key]); | ||
}, 100) | ||
); | ||
}, | ||
putLocalStorage: (key: string, value: any) => { | ||
return new Promise<void>((rs, rj) => | ||
setTimeout(() => { | ||
_cache[key] = value; | ||
rs(); | ||
}, 100) | ||
); | ||
}, | ||
removeLocalStorage: (key: string) => { | ||
return new Promise<void>((rs, rj) => | ||
setTimeout(() => { | ||
delete _cache[key]; | ||
rs(); | ||
}, 100) | ||
); | ||
}, | ||
clearAll: () => { | ||
return new Promise<void>((rs, rj) => | ||
setTimeout(() => { | ||
for (let p in _cache) delete _cache[p]; | ||
rs(); | ||
}, 100) | ||
); | ||
}, | ||
listAllLocalStorageKeys: () => { | ||
return new Promise<string[]>((rs, rj) => | ||
setTimeout(() => { | ||
rs(Object.keys(_cache)); | ||
}, 100) | ||
); | ||
}, | ||
listAllUniqueKeys: () => { | ||
return new Promise<string[]>((rs, rj) => | ||
setTimeout(() => { | ||
rs(Object.keys(_cache)); | ||
}, 100) | ||
); | ||
}, | ||
putMessage: (key: string, value: any) => { | ||
return new Promise<void>((rs, rj) => | ||
setTimeout(() => { | ||
messages.push({ key, value }); | ||
rs(); | ||
}, 100) | ||
); | ||
}, | ||
getAllMessages: (key: string, withMetadata?: boolean) => { | ||
return new Promise<any[]>((rs, rj) => | ||
setTimeout(() => { | ||
rs(messages.filter(m => m.key == key).map(m => m.value)); | ||
}, 100) | ||
); | ||
} | ||
} | ||
window['cordova'] ||= {}; | ||
window['cordova'].plugins ||= {}; | ||
window['cordova'].plugins.BEMUserCache = mockBEMUserCache; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const mockLogger = () => { | ||
window['Logger'] = { log: console.log }; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { mockBEMUserCache, mockDevice, mockGetAppVersion } from "../__mocks__/cordovaMocks"; | ||
import { addStatError, addStatEvent, addStatReading, getAppVersion, statKeys } from "../js/plugin/clientStats"; | ||
|
||
mockDevice(); | ||
// this mocks cordova-plugin-app-version, generating a "Mock App", version "1.2.3" | ||
mockGetAppVersion(); | ||
// clientStats.ts uses BEMUserCache to store the stats, so we need to mock that too | ||
mockBEMUserCache(); | ||
const db = window['cordova']?.plugins?.BEMUserCache; | ||
|
||
it('gets the app version', async () => { | ||
const ver = await getAppVersion(); | ||
expect(ver).toEqual('1.2.3'); | ||
}); | ||
|
||
it('stores a client stats reading', async () => { | ||
const reading = { a: 1, b: 2 }; | ||
await addStatReading(statKeys.REMINDER_PREFS, reading); | ||
const storedMessages = await db.getAllMessages('stats/client_time', false); | ||
expect(storedMessages).toContainEqual({ | ||
name: statKeys.REMINDER_PREFS, | ||
ts: expect.any(Number), | ||
reading, | ||
client_app_version: '1.2.3', | ||
client_os_version: '14.0.0' | ||
}); | ||
}); | ||
|
||
it('stores a client stats event', async () => { | ||
await addStatEvent(statKeys.BUTTON_FORCE_SYNC); | ||
const storedMessages = await db.getAllMessages('stats/client_nav_event', false); | ||
expect(storedMessages).toContainEqual({ | ||
name: statKeys.BUTTON_FORCE_SYNC, | ||
ts: expect.any(Number), | ||
reading: null, | ||
client_app_version: '1.2.3', | ||
client_os_version: '14.0.0' | ||
}); | ||
}); | ||
|
||
it('stores a client stats error', async () => { | ||
const errorStr = 'test error'; | ||
await addStatError(statKeys.MISSING_KEYS, errorStr); | ||
const storedMessages = await db.getAllMessages('stats/client_error', false); | ||
expect(storedMessages).toContainEqual({ | ||
name: statKeys.MISSING_KEYS, | ||
ts: expect.any(Number), | ||
reading: errorStr, | ||
client_app_version: '1.2.3', | ||
client_os_version: '14.0.0' | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { mockLogger } from '../__mocks__/globalMocks'; | ||
import { fetchUrlCached } from '../js/commHelper'; | ||
|
||
mockLogger(); | ||
|
||
// mock for JavaScript 'fetch' | ||
// we emulate a 100ms delay when i) fetching data and ii) parsing it as text | ||
global.fetch = (url: string) => new Promise((rs, rj) => { | ||
setTimeout(() => rs({ | ||
text: () => new Promise((rs, rj) => { | ||
setTimeout(() => rs('mock data for ' + url), 100); | ||
}) | ||
})); | ||
}) as any; | ||
|
||
it('fetches text from a URL and caches it so the next call is faster', async () => { | ||
const tsBeforeCalls = Date.now(); | ||
const text1 = await fetchUrlCached('https://raw.githubusercontent.com/e-mission/e-mission-phone/master/README.md'); | ||
const tsBetweenCalls = Date.now(); | ||
const text2 = await fetchUrlCached('https://raw.githubusercontent.com/e-mission/e-mission-phone/master/README.md'); | ||
const tsAfterCalls = Date.now(); | ||
expect(text1).toEqual(expect.stringContaining('mock data')); | ||
expect(text2).toEqual(expect.stringContaining('mock data')); | ||
expect(tsAfterCalls - tsBetweenCalls).toBeLessThan(tsBetweenCalls - tsBeforeCalls); | ||
}); | ||
|
||
/* The following functions from commHelper.ts are not tested because they are just wrappers | ||
around the native functions in BEMServerComm. | ||
If we wanted to test them, we would need to mock the native functions in BEMServerComm. | ||
It would be better to do integration tests that actually call the native functions. | ||
* - getRawEntries | ||
* - getRawEntriesForLocalDate | ||
* - getPipelineRangeTs | ||
* - getPipelineCompleteTs | ||
* - getMetrics | ||
* - getAggregateData | ||
* - registerUser | ||
* - updateUser | ||
* - getUser | ||
* - putOne | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { mockBEMUserCache } from "../__mocks__/cordovaMocks"; | ||
import { mockLogger } from "../__mocks__/globalMocks"; | ||
import { storageClear, storageGet, storageRemove, storageSet } from "../js/plugin/storage"; | ||
|
||
// mocks used - storage.ts uses BEMUserCache and logging. | ||
// localStorage is already mocked for us by Jest :) | ||
mockLogger(); | ||
mockBEMUserCache(); | ||
|
||
it('stores a value and retrieves it back', async () => { | ||
await storageSet('test1', 'test value 1'); | ||
const retVal = await storageGet('test1'); | ||
expect(retVal).toEqual('test value 1'); | ||
}); | ||
|
||
it('stores a value, removes it, and checks that it is gone', async () => { | ||
await storageSet('test2', 'test value 2'); | ||
await storageRemove('test2'); | ||
const retVal = await storageGet('test2'); | ||
expect(retVal).toBeUndefined(); | ||
}); | ||
|
||
it('can store objects too', async () => { | ||
const obj = { a: 1, b: 2 }; | ||
await storageSet('test6', obj); | ||
const retVal = await storageGet('test6'); | ||
expect(retVal).toEqual(obj); | ||
}); | ||
|
||
it('can also store complex nested objects with arrays', async () => { | ||
const obj = { a: 1, b: { c: [1, 2, 3] } }; | ||
await storageSet('test7', obj); | ||
const retVal = await storageGet('test7'); | ||
expect(retVal).toEqual(obj); | ||
}); | ||
|
||
it('preserves values if local gets cleared', async () => { | ||
await storageSet('test3', 'test value 3'); | ||
await storageClear({ local: true }); | ||
const retVal = await storageGet('test3'); | ||
expect(retVal).toEqual('test value 3'); | ||
}); | ||
|
||
it('preserves values if native gets cleared', async () => { | ||
await storageSet('test4', 'test value 4'); | ||
await storageClear({ native: true }); | ||
const retVal = await storageGet('test4'); | ||
expect(retVal).toEqual('test value 4'); | ||
}); | ||
|
||
it('does not preserve values if both local and native are cleared', async () => { | ||
await storageSet('test5', 'test value 5'); | ||
await storageClear({ local: true, native: true }); | ||
const retVal = await storageGet('test5'); | ||
expect(retVal).toBeUndefined(); | ||
}); | ||
|
||
it('preserves values if local gets cleared, then retrieved, then native gets cleared', async () => { | ||
await storageSet('test8', 'test value 8'); | ||
await storageClear({ local: true }); | ||
await storageGet('test8'); | ||
await storageClear({ native: true }); | ||
const retVal = await storageGet('test8'); | ||
expect(retVal).toEqual('test value 8'); | ||
}); | ||
|
||
it('preserves values if native gets cleared, then retrieved, then local gets cleared', async () => { | ||
await storageSet('test9', 'test value 9'); | ||
await storageClear({ native: true }); | ||
await storageGet('test9'); | ||
await storageClear({ local: true }); | ||
const retVal = await storageGet('test9'); | ||
expect(retVal).toEqual('test value 9'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.