From e57977f94b051eb6cbfb98ff641171965d512a67 Mon Sep 17 00:00:00 2001 From: Bartek Fabiszewski Date: Sat, 9 May 2020 22:37:03 +0200 Subject: [PATCH] Add tests --- js/src/lang.js | 2 +- js/src/layercollection.js | 5 + js/src/position.js | 4 - js/src/utils.js | 50 ++--- js/test/configdialogmodel.test.js | 293 ++++++++++++++++++++++++++++++ js/test/layercollection.test.js | 131 +++++++++++++ js/test/position.test.js | 61 +++++++ js/test/utils.test.js | 270 +++++++++++++++++++++++++++ 8 files changed, 775 insertions(+), 41 deletions(-) create mode 100644 js/test/configdialogmodel.test.js create mode 100644 js/test/layercollection.test.js create mode 100644 js/test/utils.test.js diff --git a/js/src/lang.js b/js/src/lang.js index 0581eb0..51d92fa 100644 --- a/js/src/lang.js +++ b/js/src/lang.js @@ -178,6 +178,6 @@ export default class uLang { * @return {Object.} */ getLangList() { - return this.strings['langArr']; + return this.strings['langArr'] || {}; } } diff --git a/js/src/layercollection.js b/js/src/layercollection.js index bfd08a2..4c03556 100644 --- a/js/src/layercollection.js +++ b/js/src/layercollection.js @@ -19,6 +19,9 @@ import uLayer from './layer.js'; +/** + * @extends {Array.} + */ export default class uLayerCollection extends Array { /** @@ -53,6 +56,7 @@ export default class uLayerCollection extends Array { /** * @param {number|string} id Id or listValue + * @return {uLayer} */ get(id) { if (typeof id === 'string') { @@ -70,6 +74,7 @@ export default class uLayerCollection extends Array { } /** + * Set layer with given id as priority * @param {number} id */ setPriorityLayer(id) { diff --git a/js/src/position.js b/js/src/position.js index 8371c30..fc1e7bf 100644 --- a/js/src/position.js +++ b/js/src/position.js @@ -85,10 +85,6 @@ export default class uPosition { return (this.image != null && this.image.length > 0); } - get calculatedSpeed() { - return this.seconds ? this.meters / this.seconds : 0; - } - get totalSpeed() { return this.totalSeconds ? this.totalMeters / this.totalSeconds : 0; } diff --git a/js/src/utils.js b/js/src/utils.js index 12005f5..303d8cd 100644 --- a/js/src/utils.js +++ b/js/src/utils.js @@ -46,7 +46,7 @@ export default class uUtils { const ret = fmt.replace(/%%|%s|%d/g, (match) => { if (match === '%%') { return '%'; - } else if (match === '%d' && isNaN(params[i])) { + } else if (match === '%d' && isNaN(params[i]) && typeof params[i] !== 'undefined') { throw new Error(`Wrong format specifier ${match} for ${params[i]} argument`); } if (typeof params[i] === 'undefined') { @@ -106,6 +106,11 @@ export default class uUtils { return Promise.race([ scriptLoaded, timeout ]); } + /** + * Promise rejected after given time + * @param {number} ms Time in milliseconds + * @return {Promise} + */ static timeoutPromise(ms) { return new Promise((resolve, reject) => { const tid = setTimeout(() => { @@ -135,16 +140,17 @@ export default class uUtils { * @returns {string} */ static hexToRGBA(hex, opacity) { + opacity = typeof opacity !== 'undefined' ? opacity : 1; return `rgba(${(hex = hex.replace('#', '')) .match(new RegExp(`(.{${hex.length / 3}})`, 'g')) .map((l) => parseInt(hex.length % 2 ? l + l : l, 16)) - .concat(opacity || 1).join(',')})`; + .concat(opacity).join(',')})`; } /** * Add link tag with type css * @param {string} url attribute - * @param {string} id attribute + * @param {string=} id attribute */ static addCss(url, id) { if (id && document.getElementById(id)) { @@ -166,47 +172,19 @@ export default class uUtils { */ static removeElementById(id) { const tag = document.getElementById(id); - if (tag && tag.parentNode) { - tag.parentNode.removeChild(tag); + if (tag) { + tag.remove(); } } /** * @param {string} html HTML representing a single element - * @return {Node} + * @return {Node|NodeList} */ static nodeFromHtml(html) { const template = document.createElement('template'); - template.innerHTML = html; - return template.content.firstChild; - } - - /** - * @param {string} html HTML representing a single element - * @return {NodeList} - */ - static nodesFromHtml(html) { - const template = document.createElement('template'); - template.innerHTML = html; - return template.content.childNodes; - } - - /** - * - * @param {NodeList} nodeList - * @param {string} selector - * @return {?Element} - */ - static querySelectorInList(nodeList, selector) { - for (const node of nodeList) { - if (node instanceof HTMLElement) { - const el = node.querySelector(selector); - if (el) { - return el; - } - } - } - return null; + template.innerHTML = html.trim(); + return template.content.childNodes.length > 1 ? template.content.childNodes : template.content.firstChild; } /** diff --git a/js/test/configdialogmodel.test.js b/js/test/configdialogmodel.test.js new file mode 100644 index 0000000..80e22a6 --- /dev/null +++ b/js/test/configdialogmodel.test.js @@ -0,0 +1,293 @@ +/* + * μlogger + * + * Copyright(C) 2020 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 '../src/initializer.js'; +import ConfigDialogModel from '../src/configdialogmodel.js'; +import uLayer from '../src/layer.js'; +import uLayerCollection from '../src/layercollection.js'; +import uObserve from '../src/observe.js'; + +describe('ConfigDialogModel tests', () => { + + let cm; + let layers; + + beforeEach(() => { + config.reinitialize(); + lang.init(config); + spyOn(lang, '_').and.callFake((arg) => arg); + cm = new ConfigDialogModel(); + layers = new uLayerCollection(new uLayer(0, 'layer0', '', 0), new uLayer(1, 'layer1', '', 0)); + cm.model.layers = layers; + }); + + afterEach(() => { + document.body.innerHTML = ''; + uObserve.unobserveAll(lang); + }); + + it('should show config dialog', () => { + // when + cm.init(); + // then + expect(document.querySelector('#modal')).toBeInstanceOf(HTMLDivElement); + }); + + const testElements = [ + 'interval', 'units', 'lang', 'mapApi', 'googleKey', 'layerName', 'layerId', 'layerUrl', 'initLatitude', 'initLongitude', + 'requireAuth', 'publicTracks', 'passStrength', 'passLenMin', 'strokeWeight', 'strokeColor', 'strokeOpacity', + 'colorNormal', 'colorStart', 'colorStop', 'colorExtra', 'colorHilite' + ]; + testElements.forEach((name) => { + it(`should trigger model property change for ${name}`, (done) => { + // given + cm.init(); + const element = cm.dialog.element.querySelector(`[data-bind=${name}]`); + // when + if (element instanceof HTMLSelectElement) { + const opt = document.createElement('option'); + opt.value = `__${name}__test`; + opt.text = `__${name}__test`; + element.add(opt, null); + element.value = `__${name}__test`; + } else if (element.type === 'checkbox') { + element.checked = !element.checked; + } else if (element.type === 'number') { + element.value = Math.random().toString(); + } else { + element.value = `__${name}__test`; + } + element.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(cm.model[name]).not.toBe(''); + if (element.type === 'checkbox') { + expect(cm.model[name]).toBe(element.checked); + } else { + expect(cm.model[name]).toBe(element.value); + } + done(); + }, 100); + }); + }); + + it('should show layer edit on add button click', (done) => { + // given + cm.init(); + const button = cm.getBoundElement('onLayerAdd'); + cm.layerEditEl.style.display = 'none'; + // when + button.click(); + // then + setTimeout(() => { + expect(cm.layerEditEl.style.display).toBe('block'); + expect(cm.model.layerId).toBe(-1); + done(); + }, 100); + }); + + it('should hide visible layer edit on add button click', (done) => { + // given + cm.init(); + const button = cm.getBoundElement('onLayerAdd'); + cm.onLayerAdd(); + // when + button.click(); + // then + setTimeout(() => { + expect(cm.layerEditEl.style.display).toBe('none'); + expect(cm.model.layerId).toBe(-1); + done(); + }, 100); + }); + + it('should save config on positive button clicked', (done) => { + // given + spyOn(cm, 'validate').and.returnValue(true); + spyOn(config, 'save').and.returnValue(Promise.resolve()); + cm.init(); + const button = cm.dialog.element.querySelector("[data-bind='onSave']"); + // when + button.click(); + // then + setTimeout(() => { + expect(cm.validate).toHaveBeenCalledTimes(1); + expect(config.save).toHaveBeenCalledTimes(1); + expect(document.querySelector('#modal')).toBe(null); + done(); + }, 100); + }); + + it('should hide dialog on negative button clicked', (done) => { + // given + cm.init(); + const button = cm.dialog.element.querySelector("[data-bind='onCancel']"); + // when + button.click(); + // then + setTimeout(() => { + expect(document.querySelector('#modal')).toBe(null); + done(); + }, 100); + }); + + it('should show edit on non-default layer select', (done) => { + // given + cm.model.layers = new uLayerCollection(new uLayer(0, 'layer0', '', 0), new uLayer(1, 'layer1', '', 0)); + cm.init(); + const element = cm.getBoundElement('layerId'); + // when + element.value = '1'; + element.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(cm.toggleEditEl.style.visibility).toBe('visible'); + done(); + }, 100); + }); + + it('should not show edit on default layer select', (done) => { + // given + cm.init(); + const element = cm.getBoundElement('layerId'); + // when + element.value = '0'; + element.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(cm.toggleEditEl.style.visibility).toBe('hidden'); + done(); + }, 100); + }); + + it('should delete layer on anchor click', (done) => { + // given + cm.init(); + const button = cm.dialog.element.querySelector("[data-bind='onLayerDelete']"); + const element = cm.getBoundElement('layerId'); + + expect(layers.length).toBe(2); + // when + element.value = '1'; + element.dispatchEvent(new Event('change')); + setTimeout(() => { + button.click(); + setTimeout(() => { + // then + expect(layers.length).toBe(1); + expect(layers[0].id).toBe(0); + expect(cm.model.layerId).toBe(0); + done(); + }, 100); + }, 100); + }); + + it('should add layer on anchor click', (done) => { + // given + cm.init(); + const addBtn = cm.dialog.element.querySelector("[data-bind='onLayerAdd']"); + const updateBtn = cm.dialog.element.querySelector("[data-bind='onLayerUpdate']"); + const nameEl = cm.dialog.element.querySelector("[data-bind='layerName']"); + const urlEl = cm.dialog.element.querySelector("[data-bind='layerUrl']"); + + expect(layers.length).toBe(2); + // when + addBtn.click(); + setTimeout(() => { + nameEl.value = 'test name'; + nameEl.dispatchEvent(new Event('change')); + urlEl.value = 'test url'; + urlEl.dispatchEvent(new Event('change')); + updateBtn.click(); + setTimeout(() => { + // then + expect(layers.length).toBe(3); + expect(layers[2].id).toBe(2); + done(); + }, 100); + }, 100); + }); + + it('should update layer on anchor click', (done) => { + // given + cm.init(); + const addBtn = cm.dialog.element.querySelector("[data-bind='onLayerAdd']"); + const updateBtn = cm.dialog.element.querySelector("[data-bind='onLayerUpdate']"); + const nameEl = cm.dialog.element.querySelector("[data-bind='layerName']"); + const urlEl = cm.dialog.element.querySelector("[data-bind='layerUrl']"); + const layerEl = cm.getBoundElement('layerId'); + + expect(layers.length).toBe(2); + // when + addBtn.click(); + setTimeout(() => { + layerEl.value = '1'; + layerEl.dispatchEvent(new Event('change')); + nameEl.value = 'test name'; + nameEl.dispatchEvent(new Event('change')); + urlEl.value = 'test url'; + urlEl.dispatchEvent(new Event('change')); + updateBtn.click(); + setTimeout(() => { + // then + expect(layers.length).toBe(2); + expect(layers[1].id).toBe(1); + expect(layers[1].name).toBe('test name'); + expect(layers[1].url).toBe('test url'); + done(); + }, 100); + }, 100); + }); + + it('should cancel layer edit on anchor click', (done) => { + // given + cm.init(); + const addBtn = cm.dialog.element.querySelector("[data-bind='onLayerAdd']"); + const cancelBtn = cm.dialog.element.querySelector("[data-bind='onLayerCancel']"); + const nameEl = cm.dialog.element.querySelector("[data-bind='layerName']"); + const urlEl = cm.dialog.element.querySelector("[data-bind='layerUrl']"); + const layerEl = cm.getBoundElement('layerId'); + const layersCount = layers.length; + const layerId = 1; + const layerName = layers[1].name; + const layerUrl = layers[1].url; + + expect(layers.length).toBe(2); + // when + addBtn.click(); + setTimeout(() => { + layerEl.value = layerId.toString(); + layerEl.dispatchEvent(new Event('change')); + nameEl.value = 'test name'; + nameEl.dispatchEvent(new Event('change')); + urlEl.value = 'test url'; + urlEl.dispatchEvent(new Event('change')); + cancelBtn.click(); + setTimeout(() => { + // then + expect(layers.length).toBe(layersCount); + expect(layers[1].id).toBe(layerId); + expect(layers[1].name).toBe(layerName); + expect(layers[1].url).toBe(layerUrl); + done(); + }, 100); + }, 100); + }); + +}); diff --git a/js/test/layercollection.test.js b/js/test/layercollection.test.js new file mode 100644 index 0000000..d73fdee --- /dev/null +++ b/js/test/layercollection.test.js @@ -0,0 +1,131 @@ +/* + * μlogger + * + * Copyright(C) 2020 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 uLayer from '../src/layer.js'; +import uLayerCollection from '../src/layercollection.js'; + +describe('LayerCollection tests', () => { + + let layers; + const testId = 5; + const testName = 'test name'; + const testUrl = 'https://layer.url'; + const testPriority = 0; + + beforeEach(() => { + layers = new uLayerCollection(); + }); + + it('should create instance', () => { + // then + expect(layers).toBeInstanceOf(Array); + expect(layers).toBeInstanceOf(uLayerCollection); + }); + + it('should add new layer', () => { + // when + layers.addNewLayer(testName, testUrl, testPriority); + layers.addNewLayer(`${testName}2`, `${testUrl}2`, testPriority + 1); + // then + expect(layers.length).toBe(2); + expect(layers[0]).toBeInstanceOf(uLayer); + expect(layers[0].id).toBe(1); + expect(layers[0].name).toBe(testName); + expect(layers[0].url).toBe(testUrl); + expect(layers[0].priority).toBe(testPriority); + expect(layers[1].id).toBe(2); + }); + + it('should add layer', () => { + // when + layers.addLayer(testId, testName, testUrl, testPriority); + // then + expect(layers.length).toBe(1); + expect(layers[0]).toBeInstanceOf(uLayer); + expect(layers[0].id).toBe(testId); + expect(layers[0].name).toBe(testName); + expect(layers[0].url).toBe(testUrl); + expect(layers[0].priority).toBe(testPriority); + }); + + it('should delete layer by id', () => { + // given + layers.addLayer(testId, testName, testUrl, testPriority); + layers.addLayer(testId + 1, testName, testUrl, testPriority); + + expect(layers.length).toBe(2); + // when + layers.delete(testId); + + // then + expect(layers.length).toBe(1); + expect(layers[0].id).toBe(testId + 1); + }); + + it('should get layer by id (numeric)', () => { + // when + layers.addLayer(testId, testName, testUrl, testPriority); + layers.addLayer(testId + 1, testName, testUrl, testPriority); + // then + expect(layers.get(testId).id).toBe(testId); + }); + + it('should get layer by id (string)', () => { + // when + layers.addLayer(testId + 1, testName, testUrl, testPriority); + layers.addLayer(testId, testName, testUrl, testPriority); + // then + expect(layers.get(testId.toString()).id).toBe(testId); + }); + + it('should get max id of all layers in array', () => { + // when + layers.addLayer(testId + 1, testName, testUrl, testPriority); + layers.addLayer(testId, testName, testUrl, testPriority); + // then + expect(layers.getMaxId()).toBe(testId + 1); + }); + + it('should set priority layer by id', () => { + // given + layers.addLayer(testId + 1, testName, testUrl, testPriority); + layers.addLayer(testId, testName, testUrl, testPriority); + // when + layers.setPriorityLayer(testId); + // then + expect(layers[0].priority).toBe(0); + expect(layers[1].priority).toBe(1); + expect(layers.getPriorityLayer()).toBe(testId); + }); + + it('should load layers from array', () => { + // given + const arr = [ { id: testId, name: testName, url: testUrl, priority: testPriority } ]; + // when + layers.load(arr); + // then + expect(layers.length).toBe(1); + expect(layers[0]).toBeInstanceOf(uLayer); + expect(layers[0].id).toBe(testId); + expect(layers[0].name).toBe(testName); + expect(layers[0].url).toBe(testUrl); + expect(layers[0].priority).toBe(testPriority); + }); + +}); diff --git a/js/test/position.test.js b/js/test/position.test.js index 21193b5..77ca6e4 100644 --- a/js/test/position.test.js +++ b/js/test/position.test.js @@ -17,6 +17,7 @@ * along with this program; if not, see . */ +import uAjax from '../src/ajax.js'; import uPosition from '../src/position.js'; describe('Position tests', () => { @@ -211,4 +212,64 @@ describe('Position tests', () => { expect(position.hasImage()).toBe(true); }); + it('should calculate speed', () => { + // when + const position = uPosition.fromJson(jsonPosition); + position.totalMeters = 1000; + position.totalSeconds = 10; + // then + expect(position.totalSpeed).toBe(position.totalMeters / position.totalSeconds); + }); + + it('should delete position on server', () => { + // given + spyOn(uPosition, 'update'); + const position = uPosition.fromJson(jsonPosition); + // when + position.delete() + // then + expect(uPosition.update).toHaveBeenCalledWith({ action: 'delete', posid: posId }); + }); + + it('should save position on server', () => { + // given + spyOn(uPosition, 'update'); + const position = uPosition.fromJson(jsonPosition); + // when + position.save() + // then + expect(uPosition.update).toHaveBeenCalledWith({ action: 'update', posid: posId, comment: comment }); + }); + + it('should call ajax post with url and params', () => { + // given + const url = 'utils/handleposition.php'; + spyOn(uAjax, 'post'); + const data = 'test data'; + // when + uPosition.update(data); + // then + expect(uAjax.post).toHaveBeenCalledWith(url, data); + }); + + it('should calculate distance to another position', () => { + // given + const position = uPosition.fromJson(jsonPosition); + const position2 = uPosition.fromJson(jsonPosition); + position2.latitude += 1; + position2.longitude += 1; + // then + expect(position.distanceTo(position2)).toBeCloseTo(155621.15, 2); + }); + + it('should calculate time difference to another position', () => { + // given + const timeDifference = 1234; + const position = uPosition.fromJson(jsonPosition); + const position2 = uPosition.fromJson(jsonPosition); + position.timestamp += timeDifference; + // then + expect(position.secondsTo(position2)).toBe(timeDifference); + }); + }); diff --git a/js/test/utils.test.js b/js/test/utils.test.js new file mode 100644 index 0000000..e10f8f4 --- /dev/null +++ b/js/test/utils.test.js @@ -0,0 +1,270 @@ +/* + * μlogger + * + * Copyright(C) 2020 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 uUtils from '../src/utils.js'; + +describe('Utils tests', () => { + + const name = 'test_name'; + const value = 'test_value'; + const url = 'test_url'; + const id = 'test_id'; + + beforeEach(() => { + document.cookie = `ulogger_${name}=${value}; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; + let head = document.querySelector('head'); + if (head === null) { + head = document.createElement('head'); + document.appendChild(head); + } else if (head.querySelector(`#${id}`) !== null) { + head.removeChild(head.querySelector(`#${id}`)); + } + }); + + it('should set cookie', () => { + // given + const days = 1234; + // when + uUtils.setCookie(name, value, days); + const cookie = document.cookie; + // then + expect(cookie).toContain(`${name}=${value}`); + }); + + it('should create string with format and params', () => { + // given + const stringParam = 'test'; + const numberParam = 1234; + // then + expect(uUtils.sprintf('-%s-', stringParam)).toBe(`-${stringParam}-`); + expect(uUtils.sprintf('-%d-', numberParam)).toBe(`-${numberParam}-`); + expect(uUtils.sprintf('-%d%s-', numberParam, stringParam)).toBe(`-${numberParam}${stringParam}-`); + expect(uUtils.sprintf('-%%d-')).toBe('-%d-'); + expect(() => uUtils.sprintf('-%d-')).toThrowError(/Missing argument/); + expect(() => uUtils.sprintf('-%d-', stringParam)).toThrowError(/Wrong format/); + expect(() => uUtils.sprintf('-%d-', numberParam, stringParam)).toThrowError(/Unused argument/); + }); + + it('should add script to head tag', (done) => { + // given + const head = document.querySelector('head'); + // when + uUtils.addScript(url, id, null, () => done()); + // then + expect(head.querySelector(`script#${id}`)).toBeInstanceOf(HTMLScriptElement); + expect(head.querySelector(`script#${id}`).src).toContain(url); + expect(head.querySelector(`script#${id}`).id).toBe(id); + }); + + it('should add stylesheet link to head tag', () => { + // given + const head = document.querySelector('head'); + // when + uUtils.addCss(url, id); + // then + expect(head.querySelector(`link#${id}`)).toBeInstanceOf(HTMLLinkElement); + expect(head.querySelector(`link#${id}`).href).toContain(url); + expect(head.querySelector(`link#${id}`).id).toBe(id); + expect(head.querySelector(`link#${id}`).rel).toBe('stylesheet'); + expect(head.querySelector(`link#${id}`).type).toBe('text/css'); + }); + + it('should load script', (done) => { + // given + spyOn(uUtils, 'addScript').and.callFake((_url, _id, _onload) => _onload()); + // when + uUtils.loadScript(url, id, 100) + // then + .then(() => done()) + .catch((e) => done.fail(`reject callback called: ${e}`)); + }); + + it('should fail loading script', (done) => { + // given + // eslint-disable-next-line max-params + spyOn(uUtils, 'addScript').and.callFake((_url, _id, _onload, _onerror) => _onerror(new Error(`error loading ${_id} script`))); + // when + uUtils.loadScript(url, id, 100) + // then + .then(() => done.fail('resolve callback called')) + .catch((e) => { + expect(e.message).toContain('loading'); + done(); + }); + }); + + it('should timeout loading script', (done) => { + // given + // eslint-disable-next-line max-params + spyOn(uUtils, 'addScript'); + // when + uUtils.loadScript(url, id, 1) + // then + .then(() => done.fail('resolve callback called')) + .catch((e) => { + expect(e.message).toContain('timeout'); + done(); + }); + }); + + it('should timeout promise', (done) => { + // when + uUtils.timeoutPromise(1) + // then + .then(() => done.fail('resolve callback called')) + .catch((e) => { + expect(e.message).toContain('timeout'); + done(); + }); + }); + + it('should encode html', () => { + expect(uUtils.htmlEncode('\'foo\' & "bar" ')) + .toBe(''foo' & "bar" <foobar>'); + }); + + it('should convert hex to rgba', () => { + expect(uUtils.hexToRGBA('#abcdef', 0.3)) + .toBe('rgba(171,205,239,0.3)'); + + expect(uUtils.hexToRGBA('#abc', 0)) + .toBe('rgba(170,187,204,0)'); + + expect(uUtils.hexToRGBA('#abc')) + .toBe('rgba(170,187,204,1)'); + }); + + it('should remove DOM element by id', () => { + // given + const element = document.createElement('script'); + element.id = id; + document.head.appendChild(element); + + expect(document.getElementById(id)).toBeInstanceOf(HTMLScriptElement); + // when + uUtils.removeElementById(id); + // then + expect(document.getElementById(id)).toBeNull(); + }); + + it('should create node from html string', () => { + // given + const html = `
test
`; + // when + const node = uUtils.nodeFromHtml(html); + // then + expect(node).toBeInstanceOf(HTMLDivElement); + expect(node.id).toBe(id); + expect(node.firstChild).toBeInstanceOf(HTMLSpanElement); + }); + + it('should create multiple nodes from html string', () => { + // given + const html = `
test
test2
`; + // when + const nodes = uUtils.nodeFromHtml(html); + // then + expect(nodes).toBeInstanceOf(NodeList); + expect(nodes.length).toBe(2); + expect(nodes[0].id).toBe(id); + expect(nodes[0].firstChild).toBeInstanceOf(HTMLSpanElement); + expect(nodes[1].id).toBe(`${id}_2`); + expect(nodes[1].firstChild).toBeInstanceOf(HTMLSpanElement); + }); + + it('should parse float values', () => { + expect(uUtils.getFloat('1.234')).toEqual(jasmine.any(Number)); + expect(uUtils.getFloat('1.234')).toBe(1.234); + expect(uUtils.getFloat('-1.234')).toBe(-1.234); + expect(uUtils.getFloat('-0')).toBe(0); + expect(uUtils.getFloat('1')).toBe(1); + expect(uUtils.getFloat('1a')).toBe(1); + expect(uUtils.getFloat(1.234)).toBe(1.234); + expect(uUtils.getFloat(1)).toBe(1); + expect(uUtils.getFloat(null, true)).toBeNull(); + expect(() => uUtils.getFloat(null)).toThrowError(/Invalid value/); + // eslint-disable-next-line no-undefined + expect(() => uUtils.getFloat(undefined)).toThrowError(/Invalid value/); + expect(() => uUtils.getFloat('string')).toThrowError(/Invalid value/); + expect(() => uUtils.getFloat('string', true)).toThrowError(/Invalid value/); + expect(() => uUtils.getFloat('a1')).toThrowError(/Invalid value/); + }); + + it('should parse integer values', () => { + expect(uUtils.getInteger('1234')).toEqual(jasmine.any(Number)); + expect(uUtils.getInteger('1234')).toBe(1234); + expect(uUtils.getInteger('-1234')).toBe(-1234); + expect(uUtils.getInteger('-0')).toBe(0); + expect(uUtils.getInteger('1')).toBe(1); + expect(uUtils.getInteger('1a')).toBe(1); + expect(uUtils.getInteger(1234)).toBe(1234); + expect(uUtils.getInteger(1.234)).toBe(1); + expect(uUtils.getInteger(-1.234)).toBe(-1); + expect(uUtils.getInteger(null, true)).toBeNull(); + expect(() => uUtils.getInteger(null)).toThrowError(/Invalid value/); + // eslint-disable-next-line no-undefined + expect(() => uUtils.getInteger(undefined)).toThrowError(/Invalid value/); + expect(() => uUtils.getInteger('string')).toThrowError(/Invalid value/); + expect(() => uUtils.getInteger('string', true)).toThrowError(/Invalid value/); + expect(() => uUtils.getInteger('a1')).toThrowError(/Invalid value/); + }); + + it('should parse string values', () => { + expect(uUtils.getString('1234')).toEqual(jasmine.any(String)); + expect(uUtils.getString(1234)).toEqual(jasmine.any(String)); + expect(uUtils.getString(1.234)).toEqual(jasmine.any(String)); + expect(uUtils.getString('1234')).toBe('1234'); + expect(uUtils.getString(1234)).toBe('1234'); + expect(uUtils.getString(1.234)).toBe('1.234'); + expect(uUtils.getString(-1.234)).toBe('-1.234'); + expect(uUtils.getString(null, true)).toBeNull(); + expect(() => uUtils.getString(null)).toThrowError(/Invalid value/); + // eslint-disable-next-line no-undefined + expect(() => uUtils.getString(undefined)).toThrowError(/Invalid value/); + }); + + it('should format date', () => { + // given + const date = new Date(2020, 1, 2, 3, 4, 5); + spyOn(date, 'toTimeString').and.returnValues( + '03:04:05 GMT+0200 (CEST)', + '03:04:05 GMT+0200 (Central European Standard Time)', + '03:04:05 GMT-0700 (Pacific Daylight Time)' + ); + // when + let formatted = uUtils.getTimeString(date); + // then + expect(formatted.date).toBe('2020-02-02'); + expect(formatted.time).toBe('03:04:05'); + expect(formatted.zone).toBe(' GMT+2 CEST'); + // when + formatted = uUtils.getTimeString(date); + // then + expect(formatted.zone).toBe(' GMT+2 CEST'); + // when + formatted = uUtils.getTimeString(date); + // then + expect(formatted.zone).toBe(' GMT-7 PDT'); + }); + + it('should convert degrees to radians', () => { + expect(uUtils.deg2rad(1)).toBeCloseTo(0.0174533, 7); + }); + +});