Add config view model
This commit is contained in:
parent
fcb24c3740
commit
9dd8ad007f
64
js/src/configviewmodel.js
Normal file
64
js/src/configviewmodel.js
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -53,16 +53,13 @@ export default class TrackViewModel extends ViewModel {
|
|||||||
/** @type {function} */
|
/** @type {function} */
|
||||||
onExportKml: null,
|
onExportKml: null,
|
||||||
/** @type {function} */
|
/** @type {function} */
|
||||||
onImportGpx: null,
|
onImportGpx: null
|
||||||
/** @type {function} */
|
|
||||||
onSetInterval: null
|
|
||||||
});
|
});
|
||||||
this.setClickHandlers();
|
this.setClickHandlers();
|
||||||
/** @type HTMLSelectElement */
|
/** @type HTMLSelectElement */
|
||||||
const listEl = document.querySelector('#track');
|
const listEl = document.querySelector('#track');
|
||||||
this.summaryEl = document.querySelector('#summary');
|
this.summaryEl = document.querySelector('#summary');
|
||||||
this.importEl = document.querySelector('#input-file');
|
this.importEl = document.querySelector('#input-file');
|
||||||
this.intervalEl = document.querySelector('#interval');
|
|
||||||
this.select = new uSelect(listEl);
|
this.select = new uSelect(listEl);
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.timerId = 0;
|
this.timerId = 0;
|
||||||
@ -80,7 +77,6 @@ export default class TrackViewModel extends ViewModel {
|
|||||||
this.model.onExportGpx = exportCb('gpx');
|
this.model.onExportGpx = exportCb('gpx');
|
||||||
this.model.onExportKml = exportCb('kml');
|
this.model.onExportKml = exportCb('kml');
|
||||||
this.model.onImportGpx = () => this.importEl.click();
|
this.model.onImportGpx = () => this.importEl.click();
|
||||||
this.model.onSetInterval = () => this.setAutoReloadInterval();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setObservers() {
|
setObservers() {
|
||||||
@ -119,6 +115,12 @@ export default class TrackViewModel extends ViewModel {
|
|||||||
this.loadAllUsersPosition();
|
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;
|
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() {
|
renderSummary() {
|
||||||
if (!this.state.currentTrack || !this.state.currentTrack.hasPositions) {
|
if (!this.state.currentTrack || !this.state.currentTrack.hasPositions) {
|
||||||
this.summaryEl.innerHTML = '';
|
this.summaryEl.innerHTML = '';
|
||||||
|
200
js/test/configviewmodel.test.js
Normal file
200
js/test/configviewmodel.test.js
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 = `<div id="fixture">
|
||||||
|
<div class="section">
|
||||||
|
<a id="set-interval" data-bind="onSetInterval"><span id="interval">${config.interval}</span></a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="api">api</label>
|
||||||
|
<select id="api" name="api" data-bind="mapApi">
|
||||||
|
<option value="gmaps" selected>Google Maps</option>
|
||||||
|
<option value="openlayers">OpenLayers</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="lang">lang</label>
|
||||||
|
<select id="lang" name="lang" data-bind="lang">
|
||||||
|
<option value="en" selected>English</option>
|
||||||
|
<option value="pl">Polish</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
|
<label for="units">units</label>
|
||||||
|
<select id="units" name="units" data-bind="units">
|
||||||
|
<option value="metric" selected>Metric</option>
|
||||||
|
<option value="imperial">Imperial</option>
|
||||||
|
<option value="nautical">Nautical</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -50,16 +50,11 @@ describe('TrackViewModel tests', () => {
|
|||||||
let autoReloadEl;
|
let autoReloadEl;
|
||||||
/** @type {HTMLInputElement} */
|
/** @type {HTMLInputElement} */
|
||||||
let inputFileEl;
|
let inputFileEl;
|
||||||
/** @type {HTMLSpanElement} */
|
|
||||||
let intervalEl;
|
|
||||||
/** @type {HTMLAnchorElement} */
|
|
||||||
let setIntervalEl;
|
|
||||||
let tracks;
|
let tracks;
|
||||||
let track1;
|
let track1;
|
||||||
let track2;
|
let track2;
|
||||||
let positions;
|
let positions;
|
||||||
let user;
|
let user;
|
||||||
const interval = 10;
|
|
||||||
const MAX_FILE_SIZE = 10;
|
const MAX_FILE_SIZE = 10;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -68,8 +63,6 @@ describe('TrackViewModel tests', () => {
|
|||||||
<select id="track" data-bind="currentTrackId" name="track"></select>
|
<select id="track" data-bind="currentTrackId" name="track"></select>
|
||||||
<input id="latest" type="checkbox" data-bind="showLatest">
|
<input id="latest" type="checkbox" data-bind="showLatest">
|
||||||
<input id="auto-reload" type="checkbox" data-bind="autoReload">
|
<input id="auto-reload" type="checkbox" data-bind="autoReload">
|
||||||
(<a id="set-interval" data-bind="onSetInterval"><span id="interval">${interval}</span></a> s)
|
|
||||||
<a id="set-interval">setInterval</a>
|
|
||||||
<a id="force-reload" data-bind="onReload">reload</a>
|
<a id="force-reload" data-bind="onReload">reload</a>
|
||||||
</div>
|
</div>
|
||||||
<div id="summary" class="section"></div>
|
<div id="summary" class="section"></div>
|
||||||
@ -88,6 +81,7 @@ describe('TrackViewModel tests', () => {
|
|||||||
|
|
||||||
document.body.insertAdjacentHTML('afterbegin', fixture);
|
document.body.insertAdjacentHTML('afterbegin', fixture);
|
||||||
config.initialize();
|
config.initialize();
|
||||||
|
config.interval = 10;
|
||||||
lang.init(config);
|
lang.init(config);
|
||||||
trackEl = document.querySelector('#track');
|
trackEl = document.querySelector('#track');
|
||||||
summaryEl = document.querySelector('#summary');
|
summaryEl = document.querySelector('#summary');
|
||||||
@ -97,8 +91,6 @@ describe('TrackViewModel tests', () => {
|
|||||||
importGpxEl = document.querySelector('#import-gpx');
|
importGpxEl = document.querySelector('#import-gpx');
|
||||||
forceReloadEl = document.querySelector('#force-reload');
|
forceReloadEl = document.querySelector('#force-reload');
|
||||||
inputFileEl = document.querySelector('#input-file');
|
inputFileEl = document.querySelector('#input-file');
|
||||||
intervalEl = document.querySelector('#interval');
|
|
||||||
setIntervalEl = document.querySelector('#set-interval');
|
|
||||||
autoReloadEl = document.querySelector('#auto-reload');
|
autoReloadEl = document.querySelector('#auto-reload');
|
||||||
state = new uState();
|
state = new uState();
|
||||||
vm = new TrackViewModel(state);
|
vm = new TrackViewModel(state);
|
||||||
@ -529,40 +521,17 @@ describe('TrackViewModel tests', () => {
|
|||||||
}, 100);
|
}, 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
|
// given
|
||||||
const newInterval = 99;
|
const newInterval = 99;
|
||||||
spyOn(window, 'prompt').and.returnValue(newInterval);
|
spyOn(window, 'prompt').and.returnValue(newInterval);
|
||||||
spyOn(vm, 'stopAutoReload');
|
spyOn(vm, 'stopAutoReload');
|
||||||
spyOn(vm, 'startAutoReload');
|
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;
|
vm.timerId = 1;
|
||||||
// when
|
// when
|
||||||
setIntervalEl.click();
|
config.interval = newInterval;
|
||||||
// then
|
// then
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(intervalEl.innerHTML).toBe(newInterval.toString());
|
|
||||||
expect(config.interval).toBe(newInterval);
|
|
||||||
expect(vm.stopAutoReload).toHaveBeenCalledTimes(1);
|
expect(vm.stopAutoReload).toHaveBeenCalledTimes(1);
|
||||||
expect(vm.startAutoReload).toHaveBeenCalledTimes(1);
|
expect(vm.startAutoReload).toHaveBeenCalledTimes(1);
|
||||||
done();
|
done();
|
||||||
@ -578,7 +547,7 @@ describe('TrackViewModel tests', () => {
|
|||||||
autoReloadEl.dispatchEvent(new Event('change'));
|
autoReloadEl.dispatchEvent(new Event('change'));
|
||||||
});
|
});
|
||||||
autoReloadEl.checked = false;
|
autoReloadEl.checked = false;
|
||||||
config.interval = 0.001;
|
uObserve.setSilently(config, 'interval', 0.001);
|
||||||
vm.timerId = 0;
|
vm.timerId = 0;
|
||||||
// when
|
// when
|
||||||
autoReloadEl.checked = true;
|
autoReloadEl.checked = true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user