diff --git a/js/src/userviewmodel.js b/js/src/userviewmodel.js new file mode 100644 index 0000000..6a7f1c2 --- /dev/null +++ b/js/src/userviewmodel.js @@ -0,0 +1,79 @@ +/* + * μ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 { auth, lang } from './initializer.js'; +import ViewModel from './viewmodel.js'; +import uSelect from './select.js'; +import uUser from './user.js'; +import uUtils from './utils.js'; + +/** + * @class UserViewModel + */ +export default class UserViewModel extends ViewModel { + + /** + * @param {uState} state + */ + constructor(state) { + super({ + /** @type {uUser[]} */ + userList: [], + /** @type {string} */ + currentUserId: '0' + }); + /** @type HTMLSelectElement */ + const listEl = document.querySelector('#user'); + this.select = new uSelect(listEl, lang.strings['suser'], `- ${lang.strings['allusers']} -`); + this.state = state; + this.onChanged('userList', (list) => { this.select.setOptions(list); }); + this.onChanged('currentUserId', (listValue) => { + this.state.showAllUsers = listValue === uSelect.allValue; + this.state.currentUser = this.model.userList.find((_user) => _user.listValue === listValue) || null; + }); + state.onChanged('showLatest', (showLatest) => { + if (showLatest) { + this.select.showAllOption(); + } else { + this.select.hideAllOption(); + } + }); + this.init(); + } + + init() { + this.bindAll(); + uUser.fetchList() + .then((_users) => { + this.model.userList = _users; + if (_users.length) { + let userId = _users[0].listValue; + if (auth.isAuthenticated) { + const user = this.model.userList.find((_user) => _user.listValue === auth.user.listValue); + if (user) { + userId = user.listValue; + } + } + this.model.currentUserId = userId; + } + }) + .catch((e) => { uUtils.error(e, `${lang.strings['actionfailure']}\n${e.message}`); }); + } + +} diff --git a/js/test/userviewmodel.test.js b/js/test/userviewmodel.test.js new file mode 100644 index 0000000..f37db95 --- /dev/null +++ b/js/test/userviewmodel.test.js @@ -0,0 +1,213 @@ +/* + * μ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 { auth, config, lang } from '../src/initializer.js'; +import UserViewModel from '../src/userviewmodel.js'; +import ViewModel from '../src/viewmodel.js'; +import uObserve from '../src/observe.js'; +import uSelect from '../src/select.js'; +import uState from '../src/state.js'; +import uUser from '../src/user.js'; + +describe('UserViewModel tests', () => { + + let state; + let user1; + let user2; + let users; + /** @type {HTMLSelectElement} */ + let userEl; + + beforeEach(() => { + const fixture = `
+
+ + +
+
`; + document.body.insertAdjacentHTML('afterbegin', fixture); + userEl = document.querySelector('#user'); + config.initialize(); + lang.init(config); + lang.strings['suser'] = 'select user'; + lang.strings['allusers'] = 'all users'; + state = new uState(); + user1 = new uUser(1, 'user1'); + user2 = new uUser(2, 'user2'); + users = [ user1, user2 ]; + }); + + afterEach(() => { + document.body.removeChild(document.querySelector('#fixture')); + auth.user = null; + }); + + it('should create instance with state as parameter and load user list and select first user on list', (done) => { + // given + spyOn(uUser, 'fetchList').and.returnValue(Promise.resolve(users)); + // when + const vm = new UserViewModel(state); + // then + setTimeout(() => { + expect(vm).toBeInstanceOf(ViewModel); + expect(vm.select.element).toBeInstanceOf(HTMLSelectElement); + expect(vm.state).toBe(state); + expect(vm.model.userList.length).toBe(users.length); + expect(userEl.value).toBe(user1.listValue); + expect(userEl.options.length).toBe(users.length + 1); + expect(userEl.options[1].selected).toBe(true); + expect(userEl.options[1].value).toBe(user1.listValue); + done(); + }, 100); + }); + + it('should create instance with state as parameter and load user list and select authorized user on list', (done) => { + // given + spyOn(uUser, 'fetchList').and.returnValue(Promise.resolve(users)); + // when + auth.user = user2; + const vm = new UserViewModel(state); + // then + setTimeout(() => { + expect(vm).toBeInstanceOf(ViewModel); + expect(vm.select.element).toBeInstanceOf(HTMLSelectElement); + expect(vm.state).toBe(state); + expect(vm.model.userList.length).toBe(users.length); + expect(userEl.value).toBe(user2.listValue); + expect(userEl.options.length).toBe(users.length + 1); + expect(userEl.options[2].selected).toBe(true); + expect(userEl.options[2].value).toBe(user2.listValue); + done(); + }, 100); + }); + + it('should change current user on user list option selected', (done) => { + // given + spyOn(UserViewModel.prototype, 'init'); + const vm = new UserViewModel(state); + uObserve.setSilently(state, 'currentUser', user1); + uObserve.setSilently(vm.model, 'userList', users); + uObserve.setSilently(vm.model, 'currentUserId', user1.listValue); + const options = ''; + userEl.insertAdjacentHTML('beforeend', options); + const optLength = userEl.options.length; + vm.bindAll(); + // when + userEl.value = user2.listValue; + userEl.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(state.currentUser).toBe(user2); + expect(userEl.options.length).toBe(optLength); + expect(userEl.value).toBe(user2.listValue); + expect(userEl.options[2].selected).toBe(true); + expect(userEl.options[2].value).toBe(user2.listValue); + done(); + }, 100); + }); + + it('should set showAllUsers state on "all users" option selected', (done) => { + // given + spyOn(UserViewModel.prototype, 'init'); + const vm = new UserViewModel(state); + uObserve.setSilently(state, 'currentUser', user1); + uObserve.setSilently(state, 'showAllUsers', false); + uObserve.setSilently(vm.model, 'userList', users); + uObserve.setSilently(vm.model, 'currentUserId', user1.listValue); + const options = ``; + userEl.insertAdjacentHTML('beforeend', options); + const optLength = userEl.options.length; + vm.bindAll(); + // when + userEl.value = uSelect.allValue; + userEl.dispatchEvent(new Event('change')); + // then + setTimeout(() => { + expect(state.showAllUsers).toBe(true); + expect(state.currentUser).toBe(null); + expect(userEl.value).toBe(uSelect.allValue); + expect(userEl.options.length).toBe(optLength); + expect(userEl.options[1].selected).toBe(true); + expect(userEl.options[1].value).toBe(uSelect.allValue); + done(); + }, 100); + }); + + it('should add "all users" option when "showLatest" state is set', (done) => { + // given + spyOn(UserViewModel.prototype, 'init'); + const vm = new UserViewModel(state); + uObserve.setSilently(state, 'currentUser', user1); + uObserve.setSilently(state, 'showAllUsers', false); + uObserve.setSilently(vm.model, 'userList', users); + uObserve.setSilently(vm.model, 'currentUserId', user1.listValue); + const options = ''; + userEl.insertAdjacentHTML('beforeend', options); + const optLength = userEl.options.length; + const listLength = vm.model.userList.length; + vm.bindAll(); + // when + state.showLatest = true; + // then + setTimeout(() => { + expect(state.showAllUsers).toBe(false); + expect(state.currentUser).toBe(user1); + expect(vm.select.hasAllOption).toBe(true); + expect(userEl.value).toBe(user1.listValue); + expect(userEl.options.length).toBe(optLength + 1); + expect(vm.model.userList.length).toBe(listLength); + expect(userEl.options[1].selected).toBe(false); + expect(userEl.options[1].value).toBe(uSelect.allValue); + expect(userEl.options[2].selected).toBe(true); + expect(userEl.options[2].value).toBe(user1.listValue); + done(); + }, 100); + }); + + it('should remove "all users" option when "showLatest" state is unset', (done) => { + // given + spyOn(UserViewModel.prototype, 'init'); + const vm = new UserViewModel(state); + uObserve.setSilently(state, 'currentUser', user1); + uObserve.setSilently(state, 'showAllUsers', false); + uObserve.setSilently(state, 'showLatest', true); + uObserve.setSilently(vm.model, 'userList', users); + uObserve.setSilently(vm.model, 'currentUserId', user1.listValue); + const options = ``; + userEl.insertAdjacentHTML('beforeend', options); + const optLength = userEl.options.length; + const listLength = vm.model.userList.length; + vm.bindAll(); + // when + state.showLatest = false; + // then + setTimeout(() => { + expect(state.showAllUsers).toBe(false); + expect(state.currentUser).toBe(user1); + expect(vm.select.hasAllOption).toBe(false); + expect(userEl.value).toBe(user1.listValue); + expect(userEl.options.length).toBe(optLength - 1); + expect(vm.model.userList.length).toBe(listLength); + expect(userEl.options[1].selected).toBe(true); + expect(userEl.options[1].value).toBe(user1.listValue); + done(); + }, 100); + }); + +});