/* * Copyright (C) 2018, 2019 StApps * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ import {TestBed} from '@angular/core/testing'; import {SCSetting, SCThingOriginType, SCThingType, SCSettingInputType} from '@openstapps/core'; import {ConfigProvider} from '../config/config.provider'; import {StorageProvider} from '../storage/storage.provider'; import {SettingsProvider, SettingValuesContainer, STORAGE_KEY_SETTING_VALUES} from './settings.provider'; import {Geolocation} from '@ionic-native/geolocation/ngx'; describe('SettingsProvider', () => { let configProviderSpy: jasmine.SpyObj; let settingsProvider: SettingsProvider; let storageProviderSpy: jasmine.SpyObj; beforeEach(async () => { const storageProviderMethodSpy = jasmine.createSpyObj('StorageProvider', ['init', 'get', 'has', 'put']); const configProviderMethodSpy = jasmine.createSpyObj('ConfigProvider', ['getValue']); TestBed.configureTestingModule({ imports: [], providers: [ SettingsProvider, { provide: StorageProvider, useValue: storageProviderMethodSpy, }, { provide: ConfigProvider, useValue: configProviderMethodSpy, }, Geolocation, ], }); configProviderSpy = TestBed.get(ConfigProvider); // set settings returned from config configProviderSpy.getValue.and.returnValue(Promise.resolve(CONFIG_SETTINGS_MOCK)); settingsProvider = TestBed.get(SettingsProvider); storageProviderSpy = TestBed.get(StorageProvider); storageProviderMethodSpy.has.and.returnValue(false); }); it('should provide and get setting', async () => { await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); const setting: SCSetting = await settingsProvider .getSetting(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); await expect(setting.value).toBeDefined(); }); it('should provide and get settings value', async () => { await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); const value = await settingsProvider .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue); }); it('should get persisted setting value', async () => { // set return values of storage storageProviderSpy.has.and.returnValue(Promise.resolve(true)); storageProviderSpy.get.and.returnValue(Promise.resolve(SETTING_VALUES_MOCK)); const value = await settingsProvider .getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name); await expect(value).toEqual(SETTING_VALUES_MOCK.profile.group); }); it('should set default setting value if no persisted value exist', async () => { // set return values of spy objects storageProviderSpy.has.and.returnValue(Promise.resolve(true)); storageProviderSpy.get.and.returnValue(Promise.resolve([])); const value = await settingsProvider .getValue(CONFIG_SETTINGS_MOCK[3].categories[0], CONFIG_SETTINGS_MOCK[3].name); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[3].defaultValue); }); it('should keep persisted setting values from settings that are not contained in loaded config', async () => { const settings = [ CONFIG_SETTINGS_MOCK[4], CONFIG_SETTINGS_MOCK[5], ]; configProviderSpy.getValue.and.returnValue(Promise.resolve(settings)); storageProviderSpy.has.and.returnValue(Promise.resolve(true)); storageProviderSpy.get.and.returnValue(Promise.resolve(SETTING_VALUES_MOCK)); await settingsProvider.init(); await expect(storageProviderSpy.put).toHaveBeenCalledWith(STORAGE_KEY_SETTING_VALUES, SETTING_VALUES_MOCK); }); it('should set value of a provided setting', async () => { await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[1]))); await settingsProvider .setSettingValue(CONFIG_SETTINGS_MOCK[1].categories[0], CONFIG_SETTINGS_MOCK[1].name, 'updated'); const value = await settingsProvider .getValue(CONFIG_SETTINGS_MOCK[1].categories[0], CONFIG_SETTINGS_MOCK[1].name); await expect(value).toEqual('updated'); }); it('should return copy of settingsCache', async () => { const category = CONFIG_SETTINGS_MOCK[0].categories[0]; const name = CONFIG_SETTINGS_MOCK[0].name; await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); const settings = await settingsProvider.getCache(); settings[category].settings[name].value = 'testValue'; // cached setting value should still be defaultValue await expect((await settingsProvider.getValue(category, name))) .toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue); }); it('should call storage put on setSettingValue', async () => { await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider .setSettingValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name, ''); await expect(storageProviderSpy.put).toHaveBeenCalled(); }); it('should clear settings', async () => { await settingsProvider.reset(); await expect(storageProviderSpy.put).toHaveBeenCalledWith(STORAGE_KEY_SETTING_VALUES, {}); }); it('should reset settings', async () => { const category = CONFIG_SETTINGS_MOCK[0].categories[0]; const name = CONFIG_SETTINGS_MOCK[0].name; await settingsProvider.provideSetting(JSON.parse(JSON.stringify(CONFIG_SETTINGS_MOCK[0]))); await settingsProvider.setSettingValue(category, name, 'guest'); await settingsProvider.resetDefault(); const value = await settingsProvider .getValue(CONFIG_SETTINGS_MOCK[0].categories[0], CONFIG_SETTINGS_MOCK[0].name); await expect(value).toEqual(CONFIG_SETTINGS_MOCK[0].defaultValue); }); it('should validate wrong values for inputType text', async () => { await testValue(CONFIG_SETTINGS_MOCK[0], 123456789); await testValue(CONFIG_SETTINGS_MOCK[0], false); await testValue(CONFIG_SETTINGS_MOCK[0], []); }); it('should validate wrong values for inputType password', async () => { await testValue(CONFIG_SETTINGS_MOCK[0], 123456789); await testValue(CONFIG_SETTINGS_MOCK[0], false); await testValue(CONFIG_SETTINGS_MOCK[0], []); }); it('should validate wrong values for inputType number', async () => { await testValue(CONFIG_SETTINGS_MOCK[2], ''); await testValue(CONFIG_SETTINGS_MOCK[2], false); await testValue(CONFIG_SETTINGS_MOCK[2], []); }); it('should validate wrong values for inputType singleChoice text', async () => { await testValue(CONFIG_SETTINGS_MOCK[3], ''); await testValue(CONFIG_SETTINGS_MOCK[3], 123456); await testValue(CONFIG_SETTINGS_MOCK[3], false); await testValue(CONFIG_SETTINGS_MOCK[3], []); }); it('should validate wrong values for inputType singleChoice boolean', async () => { await testValue(CONFIG_SETTINGS_MOCK[5], ''); await testValue(CONFIG_SETTINGS_MOCK[5], 123456); await testValue(CONFIG_SETTINGS_MOCK[5], []); }); it('should validate wrong values for inputType multipleChoice', async () => { await testValue(CONFIG_SETTINGS_MOCK[6], ''); await testValue(CONFIG_SETTINGS_MOCK[6], 123456); await testValue(CONFIG_SETTINGS_MOCK[6], false); await testValue(CONFIG_SETTINGS_MOCK[6], [1, 9]); }); async function testValue(setting: SCSetting, value: any) { let error: Error; await settingsProvider.provideSetting(JSON.parse(JSON.stringify(setting))); try { await settingsProvider.setSettingValue(setting.categories[0], setting.name, value); } catch (err) { error = err; } // @ts-ignore await expect(error).toBeDefined(); // @ts-ignore await expect(error.message).toMatch(/is not valid/); } const CONFIG_SETTINGS_MOCK: SCSetting[] = [ { categories: ['credentials'], defaultValue: '', inputType: SCSettingInputType.Text, name: 'username', order: 0, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Anmeldedaten'], name: 'Benutzername', }, en: { categories: ['Credentials'], name: 'Username', }, }, type: SCThingType.Setting, uid: '', }, { categories: ['credentials'], description: '', defaultValue: '', inputType: SCSettingInputType.Password, name: 'password', order: 1, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Anmeldedaten'], name: 'Passwort', }, en: { categories: ['Credentials'], name: 'Password', }, }, type: SCThingType.Setting, uid: '', }, { categories: ['profile'], description: '', defaultValue: 0, inputType: SCSettingInputType.Number, name: 'age', order: 0, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Profil'], name: 'Alter', }, en: { categories: ['Profile'], name: 'Age', }, }, type: SCThingType.Setting, uid: '', }, { categories: ['profile'], description: '', defaultValue: 'student', inputType: SCSettingInputType.SingleChoice, name: 'group', order: 1, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Benutzer'], description: 'Mit welcher Benutzergruppe soll die App verwendet werden?' + ' Die Einstellung wird beispielsweise für die Vorauswahl der Preiskategorie der Mensa verwendet.', name: 'Gruppe', }, en: { categories: ['User'], description: 'The user group the app is going to be used.' + 'This settings for example is getting used for the predefined price category of mensa meals.', name: 'Group', }, }, type: SCThingType.Setting, uid: '', values: ['student', 'employee', 'guest'], }, { categories: ['profile'], description: '', defaultValue: 'en', inputType: SCSettingInputType.SingleChoice, name: 'language', order: 0, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Benutzer'], description: 'Die Sprache in der die App angezeigt werden soll', name: 'Sprache', }, en: { categories: ['User'], description: 'The language this app is going to use', name: 'Language', }, }, type: SCThingType.Setting, uid: '', values: ['en', 'de'], }, { categories: ['privacy'], description: '', defaultValue: false, inputType: SCSettingInputType.SingleChoice, name: 'geoLocation', order: 0, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Privatsphäre'], description: 'Berechtigung für die Verwendung des Ortungsdienstes, für die Anzeige der aktuellen ' + 'Position \'\n auf der Karte und zur Berechnung der Entfernung zu Gebäuden und Orten des Campus', name: 'Position', }, en: { categories: ['Privacy'], description: 'Allow the App to use the device location to provide additional informationsbased ' + 'on your actual location', name: 'Position', }, }, type: SCThingType.Setting, uid: '', values: [true, false], }, { categories: ['others'], description: '', defaultValue: [], inputType: SCSettingInputType.MultipleChoice, name: 'numbers', order: 0, origin: { indexed: '2018-09-11T12:30:00Z', name: 'Dummy', type: SCThingOriginType.Remote, }, translations: { de: { categories: ['Sonstiges'], description: 'Test für multiple select Feld', name: 'Nummern', }, en: { categories: ['Others'], description: 'Test for multiple select field', name: 'Numbers', }, }, type: SCThingType.Setting, uid: '', values: [1, 2, 3, 4, 5, 6, 7, 8], }, ]; }); const SETTING_VALUES_MOCK: SettingValuesContainer = { foo: { bar: 'foo-bar', }, privacy: { geoLocation: 'true', }, profile: { group: 'employee', language: 'de', }, };