diff --git a/js/src/viewmodel.js b/js/src/viewmodel.js index a25a725..294e0e0 100644 --- a/js/src/viewmodel.js +++ b/js/src/viewmodel.js @@ -30,6 +30,7 @@ export default class ViewModel { */ constructor(model) { this._model = model; + this.root = document; } /** @@ -41,8 +42,10 @@ export default class ViewModel { /** * Apply bindings for model properties + * @param {HTMLElement=} root Root element */ - bindAll() { + bindAll(root = document) { + this.root = root; for (const key in this._model) { if (this._model.hasOwnProperty(key)) { this.bind(key); @@ -58,7 +61,7 @@ export default class ViewModel { */ bind(key) { const dataProp = 'bind'; - const observers = document.querySelectorAll(`[data-${dataProp}]`); + const observers = this.root.querySelectorAll(`[data-${dataProp}]`); observers.forEach(/** @param {HTMLElement} element */ (element) => { const name = element.dataset[dataProp]; if (name === key) { diff --git a/js/test/chartviewmodel.test.js b/js/test/chartviewmodel.test.js index 07f84e8..143cebd 100644 --- a/js/test/chartviewmodel.test.js +++ b/js/test/chartviewmodel.test.js @@ -19,6 +19,7 @@ import ChartViewModel from '../src/chartviewmodel.js'; import Chartist from 'chartist' +import Fixture from './helpers/fixture.js'; import TrackFactory from './helpers/trackfactory.js'; import ViewModel from '../src/viewmodel.js'; import { lang } from '../src/initializer.js'; @@ -43,6 +44,12 @@ describe('ChartViewModel tests', () => { let chartData; let chartPointNodes; + beforeEach((done) => { + Fixture.load('main.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); + beforeEach(() => { // language=XML chartFixture = ` @@ -59,15 +66,6 @@ describe('ChartViewModel tests', () => { `; - const fixture = `
-
- chart -
-
-
- close -
-
`; chartData = [ { x: 0, y: 130 }, { x: 48, y: 104 }, @@ -76,7 +74,6 @@ describe('ChartViewModel tests', () => { { x: 236, y: 118 }, { x: 387, y: 118 } ]; - document.body.insertAdjacentHTML('afterbegin', fixture); chartEl = document.querySelector('#chart'); chartContainerEl = document.querySelector('#bottom'); buttonEl = document.querySelector('#altitudes'); @@ -95,7 +92,7 @@ describe('ChartViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); uObserve.unobserveAll(lang); }); diff --git a/js/test/configviewmodel.test.js b/js/test/configviewmodel.test.js index 15d9faa..f893055 100644 --- a/js/test/configviewmodel.test.js +++ b/js/test/configviewmodel.test.js @@ -19,6 +19,7 @@ import { config, lang } from '../src/initializer.js'; import ConfigViewModel from '../src/configviewmodel.js'; +import Fixture from './helpers/fixture.js'; import ViewModel from '../src/viewmodel.js'; import uObserve from '../src/observe.js'; import uState from '../src/state.js'; @@ -43,6 +44,12 @@ describe('ConfigViewModel tests', () => { const newLang = 'pl'; const newUnits = 'imperial'; + beforeEach((done) => { + Fixture.load('main.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); + beforeEach(() => { config.reinitialize(); config.interval = 10; @@ -50,34 +57,6 @@ describe('ConfigViewModel tests', () => { config.units = 'metric'; config.mapApi = 'gmaps'; - const fixture = `
-
- ${config.interval} -
-
- - -
-
- - -
-
- - -
-
`; - document.body.insertAdjacentHTML('afterbegin', fixture); intervalEl = document.querySelector('#interval'); apiEl = document.querySelector('#api'); langEl = document.querySelector('#lang'); @@ -92,7 +71,7 @@ describe('ConfigViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); uObserve.unobserveAll(lang); }); diff --git a/js/test/fixtures/main-authorized.html b/js/test/fixtures/main-authorized.html new file mode 100644 index 0000000..c7e54e2 --- /dev/null +++ b/js/test/fixtures/main-authorized.html @@ -0,0 +1,101 @@ +
+ + +
+
+
+
+ close +
+
+ +
\ No newline at end of file diff --git a/js/test/fixtures/main.html b/js/test/fixtures/main.html new file mode 100644 index 0000000..a63958c --- /dev/null +++ b/js/test/fixtures/main.html @@ -0,0 +1,80 @@ +
+ + +
+
+
+
+ close +
+
+ +
\ No newline at end of file diff --git a/js/test/helpers/fixture.js b/js/test/helpers/fixture.js new file mode 100644 index 0000000..d39345f --- /dev/null +++ b/js/test/helpers/fixture.js @@ -0,0 +1,55 @@ +/* + * μ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 . + */ + +const baseUrl = '/base/test/fixtures/'; + +export default class Fixture { + + static load(url) { + return this.get(url).then((fixture) => { + document.body.insertAdjacentHTML('afterbegin', fixture); + }); + } + + static clear() { + document.body.innerHTML = ''; + } + + /** + * @param {string} url + * @return {Promise} + */ + static get(url) { + url = baseUrl + url; + const xhr = new XMLHttpRequest(); + return new Promise((resolve, reject) => { + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + resolve(xhr.responseText); + } else { + reject(new Error(`HTTP error ${xhr.status}`)); + } + } + }; + xhr.open('GET', url, true); + xhr.send(); + }); + } +} diff --git a/js/test/mainviewmodel.test.js b/js/test/mainviewmodel.test.js index 0863666..4d0ab93 100644 --- a/js/test/mainviewmodel.test.js +++ b/js/test/mainviewmodel.test.js @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +import Fixture from './helpers/fixture.js'; import MainViewModel from '../src/mainviewmodel.js'; import ViewModel from '../src/viewmodel.js'; import uState from '../src/state.js'; @@ -28,20 +29,20 @@ describe('MainViewModel tests', () => { let state; let menuEl; let userMenuEl; + let userButtonEl; + let menuButtonEl; + + beforeEach((done) => { + Fixture.load('main-authorized.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); beforeEach(() => { - const fixture = `
-
- user - -
- -
`; - document.body.insertAdjacentHTML('afterbegin', fixture); menuEl = document.querySelector('#menu'); userMenuEl = document.querySelector('#user-menu'); + userButtonEl = document.querySelector('a[data-bind="onShowUserMenu"]'); + menuButtonEl = document.querySelector('#menu-button a'); spyOn(window, 'addEventListener'); spyOn(window, 'removeEventListener').and.callThrough(); state = new uState(); @@ -49,7 +50,7 @@ describe('MainViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); }); it('should create instance', () => { @@ -61,10 +62,9 @@ describe('MainViewModel tests', () => { it('should hide side menu', (done) => { // given - const buttonEl = document.querySelector('#menu-button a'); vm.init(); // when - buttonEl.click(); + menuButtonEl.click(); // then setTimeout(() => { expect(menuEl.classList.contains(hiddenClass)).toBe(true); @@ -74,11 +74,10 @@ describe('MainViewModel tests', () => { it('should show side menu', (done) => { // given - const buttonEl = document.querySelector('#menu-button a'); menuEl.classList.add(hiddenClass); vm.init(); // when - buttonEl.click(); + menuButtonEl.click(); // then setTimeout(() => { expect(menuEl.classList.contains(hiddenClass)).toBe(false); @@ -88,11 +87,10 @@ describe('MainViewModel tests', () => { it('should hide user menu', (done) => { // given - const buttonEl = document.querySelector('#user-menu-button'); userMenuEl.classList.remove(hiddenClass); vm.init(); // when - buttonEl.click(); + userButtonEl.click(); // then setTimeout(() => { expect(userMenuEl.classList.contains(hiddenClass)).toBe(true); @@ -102,10 +100,9 @@ describe('MainViewModel tests', () => { it('should show user menu', (done) => { // given - const buttonEl = document.querySelector('#user-menu-button'); vm.init(); // when - buttonEl.click(); + userButtonEl.click(); // then setTimeout(() => { expect(userMenuEl.classList.contains(hiddenClass)).toBe(false); diff --git a/js/test/mapviewmodel.test.js b/js/test/mapviewmodel.test.js index 61354c8..09f85c7 100644 --- a/js/test/mapviewmodel.test.js +++ b/js/test/mapviewmodel.test.js @@ -18,6 +18,7 @@ */ import { config, lang } from '../src/initializer.js'; +import Fixture from './helpers/fixture.js'; import MapViewModel from '../src/mapviewmodel.js'; import TrackFactory from './helpers/trackfactory.js'; import ViewModel from '../src/viewmodel.js'; @@ -36,12 +37,13 @@ describe('MapViewModel tests', () => { let track; const defaultApi = 'mockApi'; + beforeEach((done) => { + Fixture.load('main.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); + beforeEach(() => { - const fixture = `
-
- -
`; - document.body.insertAdjacentHTML('afterbegin', fixture); mapEl = document.querySelector('#map-canvas'); menuButtonEl = document.querySelector('#menu-button a'); config.reinitialize(); @@ -69,7 +71,7 @@ describe('MapViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); uObserve.unobserveAll(lang); }); diff --git a/js/test/trackviewmodel.test.js b/js/test/trackviewmodel.test.js index 4ce577f..3ffcb86 100644 --- a/js/test/trackviewmodel.test.js +++ b/js/test/trackviewmodel.test.js @@ -18,6 +18,7 @@ */ import { auth, config, lang } from '../src/initializer.js'; +import Fixture from './helpers/fixture.js'; import TrackFactory from './helpers/trackfactory.js'; import TrackViewModel from '../src/trackviewmodel.js'; import ViewModel from '../src/viewmodel.js'; @@ -57,29 +58,13 @@ describe('TrackViewModel tests', () => { let user; const MAX_FILE_SIZE = 10; - beforeEach(() => { - const fixture = `
-
- - - - reload -
-
-
- kml - gpx -
-
-
- - -
- gpx -
-
`; + beforeEach((done) => { + Fixture.load('main-authorized.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); - document.body.insertAdjacentHTML('afterbegin', fixture); + beforeEach(() => { config.reinitialize(); config.interval = 10; lang.init(config); @@ -93,6 +78,8 @@ describe('TrackViewModel tests', () => { forceReloadEl = document.querySelector('#force-reload'); inputFileEl = document.querySelector('#input-file'); autoReloadEl = document.querySelector('#auto-reload'); + const maxEl = document.querySelector('input[name="MAX_FILE_SIZE"]'); + maxEl.value = MAX_FILE_SIZE; state = new uState(); vm = new TrackViewModel(state); track1 = TrackFactory.getTrack(0, { id: 1, name: 'track1' }); @@ -106,7 +93,7 @@ describe('TrackViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); uObserve.unobserveAll(lang); auth.user = null; }); diff --git a/js/test/userviewmodel.test.js b/js/test/userviewmodel.test.js index 9c2b5e9..f93165e 100644 --- a/js/test/userviewmodel.test.js +++ b/js/test/userviewmodel.test.js @@ -18,6 +18,7 @@ */ import { auth, config, lang } from '../src/initializer.js'; +import Fixture from './helpers/fixture.js'; import UserViewModel from '../src/userviewmodel.js'; import ViewModel from '../src/viewmodel.js'; import uSelect from '../src/select.js'; @@ -34,14 +35,13 @@ describe('UserViewModel tests', () => { let userEl; let vm; + beforeEach((done) => { + Fixture.load('main.html') + .then(() => done()) + .catch((e) => done.fail(e)); + }); + beforeEach(() => { - const fixture = `
-
- - -
-
`; - document.body.insertAdjacentHTML('afterbegin', fixture); userEl = document.querySelector('#user'); config.reinitialize(); lang.init(config); @@ -55,7 +55,7 @@ describe('UserViewModel tests', () => { }); afterEach(() => { - document.body.removeChild(document.querySelector('#fixture')); + Fixture.clear(); auth.user = null; }); diff --git a/karma.conf.js b/karma.conf.js index da7f9a6..a368a0c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -18,6 +18,7 @@ module.exports = function(config) { { pattern: 'test/*.test.js', type: 'module' }, { pattern: 'test/*.stub.js', type: 'module', included: false }, { pattern: 'test/helpers/*.js', type: 'module', included: false }, + { pattern: 'test/fixtures/*.html', included: false }, { pattern: 'src/**/*.js', type: 'module', included: false } ], exclude: [],