From 9a45bf4526b6905060c56721242267b2ffd7ba9a Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 23 Oct 2023 14:21:34 -0600 Subject: [PATCH 1/6] start rewrite by renaming the file --- www/js/splash/{storedevicesettings.js => storedevicesettings.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename www/js/splash/{storedevicesettings.js => storedevicesettings.ts} (100%) diff --git a/www/js/splash/storedevicesettings.js b/www/js/splash/storedevicesettings.ts similarity index 100% rename from www/js/splash/storedevicesettings.js rename to www/js/splash/storedevicesettings.ts From ce0257939cbf20595e83a394573ff5b01631ce3d Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 23 Oct 2023 14:45:44 -0600 Subject: [PATCH 2/6] update code, remove references after converting to typescript, we switch from an angular service to exported functions to handle the events, switching to exported functions called when that event is accomplished was imported to controller, but never used --- www/index.js | 1 - www/js/controllers.js | 3 +- www/js/onboarding/SaveQrPage.tsx | 3 + www/js/onboarding/SurveyPage.tsx | 3 + www/js/splash/storedevicesettings.ts | 108 +++++++++++++-------------- 5 files changed, 60 insertions(+), 58 deletions(-) diff --git a/www/index.js b/www/index.js index 0a0c63708..65faf6066 100644 --- a/www/index.js +++ b/www/index.js @@ -7,7 +7,6 @@ import './js/ngApp.js'; import './js/splash/referral.js'; import './js/splash/startprefs.js'; import './js/splash/pushnotify.js'; -import './js/splash/storedevicesettings.js'; import './js/splash/localnotify.js'; import './js/splash/remotenotify.js'; import './js/splash/notifScheduler.js'; diff --git a/www/js/controllers.js b/www/js/controllers.js index 75124efce..e484becec 100644 --- a/www/js/controllers.js +++ b/www/js/controllers.js @@ -5,7 +5,6 @@ import { addStatError, addStatReading, statKeys } from './plugin/clientStats'; angular.module('emission.controllers', ['emission.splash.startprefs', 'emission.splash.pushnotify', - 'emission.splash.storedevicesettings', 'emission.splash.localnotify', 'emission.splash.remotenotify']) @@ -14,7 +13,7 @@ angular.module('emission.controllers', ['emission.splash.startprefs', .controller('DashCtrl', function($scope) {}) .controller('SplashCtrl', function($scope, $state, $interval, $rootScope, - StartPrefs, PushNotify, StoreDeviceSettings, + StartPrefs, PushNotify, LocalNotify, RemoteNotify) { console.log('SplashCtrl invoked'); // alert("attach debugger!"); diff --git a/www/js/onboarding/SaveQrPage.tsx b/www/js/onboarding/SaveQrPage.tsx index 406376cfa..095eacc83 100644 --- a/www/js/onboarding/SaveQrPage.tsx +++ b/www/js/onboarding/SaveQrPage.tsx @@ -12,6 +12,7 @@ import { preloadDemoSurveyResponse } from "./SurveyPage"; import { storageSet } from "../plugin/storage"; import { registerUser } from "../commHelper"; import { resetDataAndRefresh } from "../config/dynamicConfig"; +import { afterConsentStore } from "../splash/storedevicesettings"; import i18next from "i18next"; const SaveQrPage = ({ }) => { @@ -28,6 +29,8 @@ const SaveQrPage = ({ }) => { setRegisterUserDone(true); preloadDemoSurveyResponse(); refreshOnboardingState(); + + afterConsentStore(); }); } else { logDebug('permissions not done, waiting'); diff --git a/www/js/onboarding/SurveyPage.tsx b/www/js/onboarding/SurveyPage.tsx index c02439cbf..1779b30e3 100644 --- a/www/js/onboarding/SurveyPage.tsx +++ b/www/js/onboarding/SurveyPage.tsx @@ -10,6 +10,7 @@ import { useTranslation } from "react-i18next"; import { DateTime } from "luxon"; import { onboardingStyles } from "./OnboardingStack"; import { displayErrorMsg } from "../plugin/logger"; +import { afterIntroStore } from "../splash/storedevicesettings"; import i18next from "i18next"; let preloadedResponsePromise: Promise = null; @@ -58,6 +59,8 @@ const SurveyPage = () => { setSurveyModalVisible(false); markIntroDone(); refreshOnboardingState(); + + afterIntroStore(); } return (<> diff --git a/www/js/splash/storedevicesettings.ts b/www/js/splash/storedevicesettings.ts index d307feaa7..45bf8c42c 100644 --- a/www/js/splash/storedevicesettings.ts +++ b/www/js/splash/storedevicesettings.ts @@ -1,62 +1,60 @@ -import angular from 'angular'; +import { getAngularService } from '../angular-react-helper'; import { updateUser } from '../commHelper'; +import { displayError, logDebug, logInfo } from '../plugin/logger'; +import i18next from 'i18next'; -angular.module('emission.splash.storedevicesettings', ['emission.plugin.logger', - 'emission.services', - 'emission.splash.startprefs']) -.factory('StoreDeviceSettings', function($window, $state, $rootScope, $ionicPlatform, - $ionicPopup, Logger, StartPrefs) { - - var storedevicesettings = {}; - - storedevicesettings.storeDeviceSettings = function() { - var lang = i18next.resolvedLanguage; - var manufacturer = $window.device.manufacturer; - var osver = $window.device.version; - return $window.cordova.getAppVersion.getVersionNumber().then(function(appver) { - var updateJSON = { - phone_lang: lang, - curr_platform: ionic.Platform.platform(), - manufacturer: manufacturer, - client_os_version: osver, - client_app_version: appver - }; - Logger.log("About to update profile with settings = "+JSON.stringify(updateJSON)); - return updateUser(updateJSON); - }).then(function(updateJSON) { - // alert("Finished saving token = "+JSON.stringify(t.token)); - }).catch(function(error) { - Logger.displayError("Error in updating profile to store device settings", error); - }); - } - - $ionicPlatform.ready().then(function() { - storedevicesettings.datacollect = $window.cordova.plugins.BEMDataCollection; - StartPrefs.readConsentState() - .then(StartPrefs.isConsented) - .then(function(consentState) { - if (consentState == true) { - storedevicesettings.storeDeviceSettings(); - } else { - Logger.log("no consent yet, waiting to store device settings in profile"); - } - }); - Logger.log("storedevicesettings startup done"); - }); +const StartPrefs = getAngularService('StartPrefs'); +const $ionicPlatform = getAngularService('$ionicPlatform'); + +let _datacollect; + +const storeDeviceSettings = function () { + var lang = i18next.resolvedLanguage; + var manufacturer = window['device'].manufacturer; + var osver = window['device'].version; + return window['cordova'].getAppVersion.getVersionNumber().then(function (appver) { + var updateJSON = { + phone_lang: lang, + curr_platform: window['cordova'].platformId, + manufacturer: manufacturer, + client_os_version: osver, + client_app_version: appver + }; + logDebug("About to update profile with settings = " + JSON.stringify(updateJSON)); + return updateUser(updateJSON); + }).then(function (updateJSON) { + // alert("Finished saving token = "+JSON.stringify(t.token)); + }).catch(function (error) { + displayError(error, "Error in updating profile to store device settings"); + }); +} - $rootScope.$on(StartPrefs.CONSENTED_EVENT, function(event, data) { - console.log("got consented event "+JSON.stringify(event.name) - +" with data "+ JSON.stringify(data)); - if (StartPrefs.isIntroDone()) { - console.log("intro is done -> reconsent situation, we already have a token -> register"); - storedevicesettings.storeDeviceSettings(); +const initDeviceSettings = function () { + _datacollect = window['cordova'].plugins.BEMDataCollection; + StartPrefs.readConsentState() + .then(StartPrefs.isConsented) + .then(function (consentState) { + if (consentState == true) { + storeDeviceSettings(); + } else { + logInfo("no consent yet, waiting to store device settings in profile"); } }); + logInfo("storedevicesettings startup done"); +} - $rootScope.$on(StartPrefs.INTRO_DONE_EVENT, function(event, data) { - console.log("intro is done -> original consent situation, we should have a token by now -> register"); - storedevicesettings.storeDeviceSettings(); - }); +$ionicPlatform.ready().then(initDeviceSettings); + +export const afterConsentStore = function () { + console.log("in storedevicesettings, executing after consent is received"); + if (StartPrefs.isIntroDone()) { + console.log("intro is done -> reconsent situation, we already have a token -> register"); + storeDeviceSettings(); + } +}; + +export const afterIntroStore = function () { + console.log("intro is done -> original consent situation, we should have a token by now -> register"); + storeDeviceSettings(); +}; - return storedevicesettings; -}); From 4c0aeb58276ed4eaea6f16b71166d868383c7e6b Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 23 Oct 2023 15:27:51 -0600 Subject: [PATCH 3/6] resolve merge issue when I merged my startprefs branch, I forgot to update storedevicesettings to reflect the removal of that angular service --- www/js/splash/storedevicesettings.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/www/js/splash/storedevicesettings.ts b/www/js/splash/storedevicesettings.ts index 45bf8c42c..6a356f7d6 100644 --- a/www/js/splash/storedevicesettings.ts +++ b/www/js/splash/storedevicesettings.ts @@ -1,9 +1,10 @@ import { getAngularService } from '../angular-react-helper'; import { updateUser } from '../commHelper'; import { displayError, logDebug, logInfo } from '../plugin/logger'; +import { readConsentState, isConsented } from './startprefs'; +import { readIntroDone } from '../onboarding/onboardingHelper'; import i18next from 'i18next'; -const StartPrefs = getAngularService('StartPrefs'); const $ionicPlatform = getAngularService('$ionicPlatform'); let _datacollect; @@ -31,8 +32,8 @@ const storeDeviceSettings = function () { const initDeviceSettings = function () { _datacollect = window['cordova'].plugins.BEMDataCollection; - StartPrefs.readConsentState() - .then(StartPrefs.isConsented) + readConsentState() + .then(isConsented) .then(function (consentState) { if (consentState == true) { storeDeviceSettings(); @@ -47,7 +48,7 @@ $ionicPlatform.ready().then(initDeviceSettings); export const afterConsentStore = function () { console.log("in storedevicesettings, executing after consent is received"); - if (StartPrefs.isIntroDone()) { + if (readIntroDone()) { console.log("intro is done -> reconsent situation, we already have a token -> register"); storeDeviceSettings(); } From 589eca93eac8fb8df59870e9ea6b94e64049c6a1 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 23 Oct 2023 15:41:24 -0600 Subject: [PATCH 4/6] change "ready" handling since there is no angular code in this file, can't use $ionicPlatform instead call the init function from the App code --- www/js/App.tsx | 2 ++ www/js/splash/storedevicesettings.ts | 7 +------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/www/js/App.tsx b/www/js/App.tsx index b1806a5cc..efe58304c 100644 --- a/www/js/App.tsx +++ b/www/js/App.tsx @@ -11,6 +11,7 @@ import { OnboardingRoute, OnboardingState, getPendingOnboardingState } from './o import { setServerConnSettings } from './config/serverConn'; import AppStatusModal from './control/AppStatusModal'; import usePermissionStatus from './usePermissionStatus'; +import { initDeviceSettings } from './splash/storeDeviceSettings'; const defaultRoutes = (t) => [ { key: 'label', title: t('diary.label-tab'), focusedIcon: 'check-bold', unfocusedIcon: 'check-outline' }, @@ -48,6 +49,7 @@ const App = () => { useEffect(() => { if (!appConfig) return; setServerConnSettings(appConfig).then(() => { + initDeviceSettings(); refreshOnboardingState(); }); }, [appConfig]); diff --git a/www/js/splash/storedevicesettings.ts b/www/js/splash/storedevicesettings.ts index 6a356f7d6..ebbfdd5af 100644 --- a/www/js/splash/storedevicesettings.ts +++ b/www/js/splash/storedevicesettings.ts @@ -1,12 +1,9 @@ -import { getAngularService } from '../angular-react-helper'; import { updateUser } from '../commHelper'; import { displayError, logDebug, logInfo } from '../plugin/logger'; import { readConsentState, isConsented } from './startprefs'; import { readIntroDone } from '../onboarding/onboardingHelper'; import i18next from 'i18next'; -const $ionicPlatform = getAngularService('$ionicPlatform'); - let _datacollect; const storeDeviceSettings = function () { @@ -30,7 +27,7 @@ const storeDeviceSettings = function () { }); } -const initDeviceSettings = function () { +export const initDeviceSettings = function () { _datacollect = window['cordova'].plugins.BEMDataCollection; readConsentState() .then(isConsented) @@ -44,8 +41,6 @@ const initDeviceSettings = function () { logInfo("storedevicesettings startup done"); } -$ionicPlatform.ready().then(initDeviceSettings); - export const afterConsentStore = function () { console.log("in storedevicesettings, executing after consent is received"); if (readIntroDone()) { From 59ca64d53a4bf7d0031f320173ea225e10b5ee53 Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Mon, 23 Oct 2023 15:47:19 -0600 Subject: [PATCH 5/6] updated file name --- www/js/onboarding/SaveQrPage.tsx | 2 +- www/js/onboarding/SurveyPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/www/js/onboarding/SaveQrPage.tsx b/www/js/onboarding/SaveQrPage.tsx index f141b59cb..16b8550d4 100644 --- a/www/js/onboarding/SaveQrPage.tsx +++ b/www/js/onboarding/SaveQrPage.tsx @@ -12,7 +12,7 @@ import { preloadDemoSurveyResponse } from "./SurveyPage"; import { storageSet } from "../plugin/storage"; import { registerUser } from "../commHelper"; import { resetDataAndRefresh } from "../config/dynamicConfig"; -import { afterConsentStore } from "../splash/storedevicesettings"; +import { afterConsentStore } from "../splash/storeDeviceSettings"; import { markConsented } from "../splash/startprefs"; import i18next from "i18next"; diff --git a/www/js/onboarding/SurveyPage.tsx b/www/js/onboarding/SurveyPage.tsx index 1779b30e3..61ee2d69a 100644 --- a/www/js/onboarding/SurveyPage.tsx +++ b/www/js/onboarding/SurveyPage.tsx @@ -10,7 +10,7 @@ import { useTranslation } from "react-i18next"; import { DateTime } from "luxon"; import { onboardingStyles } from "./OnboardingStack"; import { displayErrorMsg } from "../plugin/logger"; -import { afterIntroStore } from "../splash/storedevicesettings"; +import { afterIntroStore } from "../splash/storeDeviceSettings"; import i18next from "i18next"; let preloadedResponsePromise: Promise = null; From 78b53364ed647335cdeacbf2c456237528c316ea Mon Sep 17 00:00:00 2001 From: Abby Wheelis Date: Tue, 24 Oct 2023 12:52:29 -0600 Subject: [PATCH 6/6] implement tests for storedevicesettings while testing, there were some new or updated mocks needed, as well as some updates to the file in order to allow the tests to wait for these methods to run before testing for the results I don't think we need to await the functions as they're used, however, because it's ok if they run asyncronously in context --- www/__mocks__/cordovaMocks.ts | 19 ++++++ www/__tests__/storeDeviceSettings.test.ts | 76 +++++++++++++++++++++++ www/js/splash/storedevicesettings.ts | 19 +++--- 3 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 www/__tests__/storeDeviceSettings.test.ts diff --git a/www/__mocks__/cordovaMocks.ts b/www/__mocks__/cordovaMocks.ts index c00377120..f974b9e0d 100644 --- a/www/__mocks__/cordovaMocks.ts +++ b/www/__mocks__/cordovaMocks.ts @@ -63,6 +63,7 @@ export const mockBEMUserCache = () => { return new Promise((rs, rj) => setTimeout(() => { for (let p in _cache) delete _cache[p]; + for (let doc in _storage) delete _storage[doc]; rs(); }, 100) ); @@ -129,3 +130,21 @@ export const mockBEMDataCollection = () => { window['cordova'] ||= {}; window['cordova'].plugins.BEMDataCollection = mockBEMDataCollection; } + +export const mockBEMServerCom = () => { + const mockBEMServerCom = { + postUserPersonalData: (actionString, typeString, updateDoc, rs, rj) => { + setTimeout(() => { + _storage["user_data"] = updateDoc; + rs(); + }, 100) + }, + + getUserPersonalData: (actionString, rs, rj) => { + setTimeout(() => { + rs( _storage["user_data"] ); + }, 100) + } + } + window['cordova'].plugins.BEMServerComm = mockBEMServerCom; +} diff --git a/www/__tests__/storeDeviceSettings.test.ts b/www/__tests__/storeDeviceSettings.test.ts new file mode 100644 index 000000000..4ea77c7eb --- /dev/null +++ b/www/__tests__/storeDeviceSettings.test.ts @@ -0,0 +1,76 @@ +import { initDeviceSettings, afterConsentStore, afterIntroStore } from '../js/splash/storedevicesettings'; +import { mockBEMUserCache, mockDevice, mockGetAppVersion, mockBEMDataCollection, mockBEMServerCom, mockCordova } from '../__mocks__/cordovaMocks'; +import { mockLogger } from '../__mocks__/globalMocks'; +import { storageClear } from '../js/plugin/storage'; +import { getUser } from '../js/commHelper'; +import { markConsented, readConsentState } from '../js/splash/startprefs'; +import { markIntroDone } from '../js/onboarding/onboardingHelper'; + +mockBEMUserCache(); +mockDevice(); +mockCordova(); +mockLogger(); +mockGetAppVersion(); +mockBEMDataCollection(); +mockBEMServerCom(); + +global.fetch = (url: string) => new Promise((rs, rj) => { + setTimeout(() => rs({ + json: () => new Promise((rs, rj) => { + let myJSON = { "emSensorDataCollectionProtocol": { "protocol_id": "2014-04-6267", "approval_date": "2016-07-14" } }; + setTimeout(() => rs(myJSON), 100); + }) + })); +}) as any; + +it('runs after consent is marked', async () => { + await storageClear({ local: true, native: true }); + await readConsentState(); + await markConsented(); + await initDeviceSettings(); + let user = await getUser(); + expect(user).toMatchObject({ + client_os_version: '14.0.0', + client_app_version: '1.2.3' + }) +}); + +it('does not run if consent is not marked', async () => { + await storageClear({ local: true, native: true }); + await readConsentState(); + await initDeviceSettings(); + let user = await getUser(); + expect(user).toBeUndefined(); +}); + +it('does not run after consent, if intro not done', async () => { + await storageClear({ local: true, native: true }); + await afterConsentStore(); + let user = await getUser(); + expect(user).toBeUndefined(); +}) + +it('runs after consent, if intro done', async () => { + await storageClear({ local: true, native: true }); + await markIntroDone(); + await afterConsentStore(); + setTimeout(async () => { + console.log("hello world"); + getUser().then((user) => { + expect(user).toMatchObject({ + client_os_version: '14.0.0', + client_app_version: '1.2.3' + }) + }); + }, 5000); +}); + +it('runs after intro is done', async () => { + await storageClear({ local: true, native: true }); + await afterIntroStore(); + let user = await getUser(); + expect(user).toMatchObject({ + client_os_version: '14.0.0', + client_app_version: '1.2.3' + }) +}); \ No newline at end of file diff --git a/www/js/splash/storedevicesettings.ts b/www/js/splash/storedevicesettings.ts index ebbfdd5af..3aef75d6e 100644 --- a/www/js/splash/storedevicesettings.ts +++ b/www/js/splash/storedevicesettings.ts @@ -29,28 +29,31 @@ const storeDeviceSettings = function () { export const initDeviceSettings = function () { _datacollect = window['cordova'].plugins.BEMDataCollection; - readConsentState() + return readConsentState() .then(isConsented) .then(function (consentState) { if (consentState == true) { - storeDeviceSettings(); + return storeDeviceSettings(); } else { logInfo("no consent yet, waiting to store device settings in profile"); } + logInfo("storedevicesettings startup done"); }); - logInfo("storedevicesettings startup done"); } export const afterConsentStore = function () { console.log("in storedevicesettings, executing after consent is received"); - if (readIntroDone()) { - console.log("intro is done -> reconsent situation, we already have a token -> register"); - storeDeviceSettings(); - } + readIntroDone().then((introDone) => { + console.log("intro is done?", introDone); + if (introDone) { + console.log("intro is done -> reconsent situation, we already have a token -> register"); + return storeDeviceSettings(); + } + }); }; export const afterIntroStore = function () { console.log("intro is done -> original consent situation, we should have a token by now -> register"); - storeDeviceSettings(); + return storeDeviceSettings(); };