diff --git a/js/src/configviewmodel.js b/js/src/configviewmodel.js new file mode 100644 index 0000000..a219138 --- /dev/null +++ b/js/src/configviewmodel.js @@ -0,0 +1,64 @@ +/* + * μlogger + * + * Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net) + * + * This 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 { config, lang } from './initializer.js'; +import ViewModel from './viewmodel.js'; +import uUtils from './utils.js'; + +/** + * @class ConfigViewModel + */ +export default class ConfigViewModel extends ViewModel { + /** + * @param {uState} state + */ + constructor(state) { + super(config); + this.state = state; + this.intervalEl = document.querySelector('#interval'); + this.model.onSetInterval = () => this.setAutoReloadInterval(); + this.bindAll(); + this.onChanged('mapApi', (api) => { + uUtils.setCookie('api', api); + }); + this.onChanged('lang', (_lang) => { + uUtils.setCookie('lang', _lang); + ConfigViewModel.reload(); + }); + this.onChanged('units', (units) => { + uUtils.setCookie('units', units); + ConfigViewModel.reload(); + }); + this.onChanged('interval', (interval) => { + this.intervalEl.innerHTML = interval.toString(); + uUtils.setCookie('interval', interval); + }); + } + + static reload() { + window.location.reload(); + } + + setAutoReloadInterval() { + const interval = parseInt(prompt(lang.strings['newinterval'])); + if (!isNaN(interval) && interval !== this.model.interval) { + this.model.interval = interval; + } + } +} diff --git a/js/src/trackviewmodel.js b/js/src/trackviewmodel.js index ceee88b..2ba5285 100644 --- a/js/src/trackviewmodel.js +++ b/js/src/trackviewmodel.js @@ -53,16 +53,13 @@ export default class TrackViewModel extends ViewModel { /** @type {function} */ onExportKml: null, /** @type {function} */ - onImportGpx: null, - /** @type {function} */ - onSetInterval: null + onImportGpx: null }); this.setClickHandlers(); /** @type HTMLSelectElement */ const listEl = document.querySelector('#track'); this.summaryEl = document.querySelector('#summary'); this.importEl = document.querySelector('#input-file'); - this.intervalEl = document.querySelector('#interval'); this.select = new uSelect(listEl); this.state = state; this.timerId = 0; @@ -80,7 +77,6 @@ export default class TrackViewModel extends ViewModel { this.model.onExportGpx = exportCb('gpx'); this.model.onExportKml = exportCb('kml'); this.model.onImportGpx = () => this.importEl.click(); - this.model.onSetInterval = () => this.setAutoReloadInterval(); } setObservers() { @@ -119,6 +115,12 @@ export default class TrackViewModel extends ViewModel { this.loadAllUsersPosition(); } }); + config.onChanged('interval', () => { + if (this.timerId) { + this.stopAutoReload(); + this.startAutoReload(); + } + }); } /** @@ -275,19 +277,6 @@ export default class TrackViewModel extends ViewModel { this.model.autoReload = false; } - setAutoReloadInterval() { - const interval = parseInt(prompt(lang.strings['newinterval'])); - if (!isNaN(interval) && interval !== config.interval) { - config.interval = interval; - this.intervalEl.innerHTML = config.interval.toString(); - // if live tracking on, reload with new interval - if (this.timerId) { - this.stopAutoReload(); - this.startAutoReload(); - } - } - } - renderSummary() { if (!this.state.currentTrack || !this.state.currentTrack.hasPositions) { this.summaryEl.innerHTML = ''; diff --git a/js/test/configviewmodel.test.js b/js/test/configviewmodel.test.js new file mode 100644 index 0000000..7691de6 --- /dev/null +++ b/js/test/configviewmodel.test.js @@ -0,0 +1,200 @@ +/* + * μlogger + * + * Copyright(C) 2019 Bartek Fabiszewski (www.fabiszewski.net) + * + * This 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; either version 3 of the License, or + * (at your option) any later version. + * + * 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 ConfigViewModel from '../src/configviewmodel.js'; +import ViewModel from '../src/viewmodel.js'; +import { config } from '../src/initializer.js'; +import uObserve from '../src/observe.js'; +import uState from '../src/state.js'; +import uUtils from '../src/utils.js'; + +describe('ConfigViewModel tests', () => { + + let vm; + let state; + /** @type {HTMLSpanElement} */ + let intervalEl; + /** @type {HTMLAnchorElement} */ + let setIntervalEl; + /** @type {HTMLSelectElement} */ + let apiEl; + /** @type {HTMLSelectElement} */ + let langEl; + /** @type {HTMLSelectElement} */ + let unitsEl; + const newInterval = 99; + const newMapApi = 'openlayers'; + const newLang = 'pl'; + const newUnits = 'imperial'; + + beforeEach(() => { + config.initialize(); + uObserve.setSilently(config, 'interval', 10); + uObserve.setSilently(config, 'lang', 'en'); + uObserve.setSilently(config, 'units', 'metric'); + uObserve.setSilently(config, 'mapApi', 'gmaps'); + + const fixture = `
+ +
+ + +
+
+ + +
+
+ + +
+
`; + document.body.insertAdjacentHTML('afterbegin', fixture); + intervalEl = document.querySelector('#interval'); + apiEl = document.querySelector('#api'); + langEl = document.querySelector('#lang'); + unitsEl = document.querySelector('#units'); + setIntervalEl = document.querySelector('#set-interval'); + state = new uState(); + vm = new ConfigViewModel(state); + spyOn(uUtils, 'setCookie').and.returnValue(newInterval); + spyOn(ConfigViewModel, 'reload'); + }); + + afterEach(() => { + document.body.removeChild(document.querySelector('#fixture')); + uObserve.unobserveAll(config); + }); + + it('should create instance with state as parameter', () => { + // then + expect(vm).toBeInstanceOf(ViewModel); + expect(vm.state).toBe(state); + }); + + it('should get interval value from user prompt on interval click', (done) => { + // given + spyOn(window, 'prompt').and.returnValue(newInterval); + // when + setIntervalEl.click(); + // then + setTimeout(() => { + expect(intervalEl.innerHTML).toBe(newInterval.toString()); + expect(config.interval).toBe(newInterval); + done(); + }, 100); + }); + + it('should update UI text and set cookie on config interval change', (done) => { + // when + config.interval = newInterval; + // then + setTimeout(() => { + expect(intervalEl.innerHTML).toBe(newInterval.toString()); + expect(uUtils.setCookie).toHaveBeenCalledWith('interval', newInterval); + done(); + }, 100); + }); + + it('should update select value on config map API change', (done) => { + // when + config.mapApi = newMapApi; + // then + setTimeout(() => { + expect(apiEl.value).toBe(newMapApi); + expect(uUtils.setCookie).toHaveBeenCalledWith('api', newMapApi); + done(); + }, 100); + }); + + it('should update config map API on select value change', (done) => { + // when + apiEl.value = newMapApi; + apiEl.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(config.mapApi).toBe(newMapApi); + expect(uUtils.setCookie).toHaveBeenCalledWith('api', newMapApi); + done(); + }, 100); + }); + + it('should update select value and do reload on config language change', (done) => { + // when + config.lang = newLang; + // then + setTimeout(() => { + expect(langEl.value).toBe(newLang); + expect(uUtils.setCookie).toHaveBeenCalledWith('lang', newLang); + expect(ConfigViewModel.reload).toHaveBeenCalledTimes(1); + done(); + }, 100); + }); + + it('should update config language and do reload on select value change', (done) => { + // when + langEl.value = newLang; + langEl.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(config.lang).toBe(newLang); + expect(uUtils.setCookie).toHaveBeenCalledWith('lang', newLang); + expect(ConfigViewModel.reload).toHaveBeenCalledTimes(1); + done(); + }, 100); + }); + + it('should update select value and do reload on config units change', (done) => { + // when + config.units = newUnits; + // then + setTimeout(() => { + expect(unitsEl.value).toBe(newUnits); + expect(uUtils.setCookie).toHaveBeenCalledWith('units', newUnits); + expect(ConfigViewModel.reload).toHaveBeenCalledTimes(1); + done(); + }, 100); + }); + + it('should update config units and do reload on select value change', (done) => { + // when + unitsEl.value = newUnits; + unitsEl.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(config.units).toBe(newUnits); + expect(uUtils.setCookie).toHaveBeenCalledWith('units', newUnits); + expect(ConfigViewModel.reload).toHaveBeenCalledTimes(1); + done(); + }, 100); + }); + +}); diff --git a/js/test/trackviewmodel.test.js b/js/test/trackviewmodel.test.js index 5616b0a..c937564 100644 --- a/js/test/trackviewmodel.test.js +++ b/js/test/trackviewmodel.test.js @@ -50,16 +50,11 @@ describe('TrackViewModel tests', () => { let autoReloadEl; /** @type {HTMLInputElement} */ let inputFileEl; - /** @type {HTMLSpanElement} */ - let intervalEl; - /** @type {HTMLAnchorElement} */ - let setIntervalEl; let tracks; let track1; let track2; let positions; let user; - const interval = 10; const MAX_FILE_SIZE = 10; beforeEach(() => { @@ -68,8 +63,6 @@ describe('TrackViewModel tests', () => { - (${interval} s) - setInterval reload
@@ -88,6 +81,7 @@ describe('TrackViewModel tests', () => { document.body.insertAdjacentHTML('afterbegin', fixture); config.initialize(); + config.interval = 10; lang.init(config); trackEl = document.querySelector('#track'); summaryEl = document.querySelector('#summary'); @@ -97,8 +91,6 @@ describe('TrackViewModel tests', () => { importGpxEl = document.querySelector('#import-gpx'); forceReloadEl = document.querySelector('#force-reload'); inputFileEl = document.querySelector('#input-file'); - intervalEl = document.querySelector('#interval'); - setIntervalEl = document.querySelector('#set-interval'); autoReloadEl = document.querySelector('#auto-reload'); state = new uState(); vm = new TrackViewModel(state); @@ -529,40 +521,17 @@ describe('TrackViewModel tests', () => { }, 100); }); - it('should get interval value from user prompt on interval click', (done) => { + it('should restart running auto-reload on config interval change', (done) => { // given const newInterval = 99; spyOn(window, 'prompt').and.returnValue(newInterval); spyOn(vm, 'stopAutoReload'); spyOn(vm, 'startAutoReload'); - config.interval = interval; - vm.timerId = 0; - // when - setIntervalEl.click(); - // then - setTimeout(() => { - expect(intervalEl.innerHTML).toBe(newInterval.toString()); - expect(config.interval).toBe(newInterval); - expect(vm.stopAutoReload).not.toHaveBeenCalled(); - expect(vm.startAutoReload).not.toHaveBeenCalled(); - done(); - }, 100); - }); - - it('should get interval value from user prompt on interval click and restart running auto-reload', (done) => { - // given - const newInterval = 99; - spyOn(window, 'prompt').and.returnValue(newInterval); - spyOn(vm, 'stopAutoReload'); - spyOn(vm, 'startAutoReload'); - config.interval = interval; vm.timerId = 1; // when - setIntervalEl.click(); + config.interval = newInterval; // then setTimeout(() => { - expect(intervalEl.innerHTML).toBe(newInterval.toString()); - expect(config.interval).toBe(newInterval); expect(vm.stopAutoReload).toHaveBeenCalledTimes(1); expect(vm.startAutoReload).toHaveBeenCalledTimes(1); done(); @@ -578,7 +547,7 @@ describe('TrackViewModel tests', () => { autoReloadEl.dispatchEvent(new Event('change')); }); autoReloadEl.checked = false; - config.interval = 0.001; + uObserve.setSilently(config, 'interval', 0.001); vm.timerId = 0; // when autoReloadEl.checked = true;