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 = `
`;
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 = `
-
-
- api
-
- Google Maps
- OpenLayers
-
-
-
- lang
-
- English
- Polish
-
-
-
- units
-
- Metric
- Imperial
- Nautical
-
-
-
`;
- 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 @@
+
\ 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 @@
+
\ 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 = ``;
- 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 = ``;
+ 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: [],