From 12bf269908f3a7923b129be4f779a32390bb3f7a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 19 Apr 2021 23:02:50 +0200 Subject: [PATCH 01/12] use vuex-persistedstate instead of cookies. --- frontend/package.json | 3 +- frontend/src/App.vue | 24 +--------- frontend/src/main.js | 8 ++++ frontend/src/routes/router.js | 9 ++-- frontend/src/routes/routes.js | 23 +++++++-- frontend/src/store/store.js | 47 ++++++------------- frontend/src/views/Layout/DashboardNavbar.vue | 2 +- frontend/src/views/Pages/Login.vue | 21 ++++++--- frontend/yarn.lock | 13 +++++ 9 files changed, 77 insertions(+), 73 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 80450b49b..49f2173d5 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -75,7 +75,8 @@ "vue-qrcode-reader": "^2.3.16", "vue-router": "^3.0.6", "vue2-transitions": "^0.2.3", - "vuex": "^3.6.0" + "vuex": "^3.6.0", + "vuex-persistedstate": "^4.0.0-beta.3" }, "devDependencies": { "@vue/cli-plugin-babel": "^3.7.0", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index fafd46b32..f0b7e513c 100755 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -17,7 +17,7 @@
- +
@@ -38,31 +38,9 @@ export default { }, data() { return { - session_id: null, - email: '', language: 'en', } }, - created() { - //console.log('xx', $cookies.get('gdd_lang')) - //console.log('%cWillkommen bei Gradido %cgreen text', 'font-weight:bold', 'color: green') - if ($cookies.isKey('gdd_session_id') == true) { - //console.log('%cHey %c' + $cookies.get('gdd_u') + '', 'font-weight:bold', 'color: orange') - this.$store.commit('session_id', $cookies.get('gdd_session_id')) - this.$store.commit('email', $cookies.get('gdd_u')) - if ($cookies.get('gdd_lang') != 'de' || $cookies.get('gdd_lang') != 'de') { - this.$store.commit('language', 'de') - } else { - this.$store.commit('language', $cookies.get('gdd_lang')) - } - - this.$i18n.locale = $cookies.get('gdd_lang') - this.$router.push('overview') - } else { - //console.log('app.vue to Logout') - this.$store.dispatch('logout') - } - }, data() { return { config: { diff --git a/frontend/src/main.js b/frontend/src/main.js index 9b5ff9cba..03c522655 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -16,6 +16,14 @@ Vue.use(DashboardPlugin) Vue.config.productionTip = false Vue.use(VueCookies) +router.beforeEach((to, from, next) => { + if (to.meta.requiresAuth && !store.state.session_id) { + next({ path: '/login' }) + } else { + next() + } +}) + /* eslint-disable no-new */ new Vue({ el: '#app', diff --git a/frontend/src/routes/router.js b/frontend/src/routes/router.js index cd3646fe0..93892801d 100644 --- a/frontend/src/routes/router.js +++ b/frontend/src/routes/router.js @@ -1,6 +1,7 @@ import Vue from 'vue' import VueRouter from 'vue-router' import routes from './routes' +import { store } from '../store/store' Vue.use(VueRouter) @@ -21,11 +22,11 @@ const router = new VueRouter({ }) router.beforeEach((to, from, next) => { - let language = to.params.lang - if (!language) { - language = 'de' + if (to.meta.requiresAuth && !store.state.session_id) { + next({ path: '/login' }) + } else { + next() } - next() }) export default router diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index b50f2a7b1..dc30e5754 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -1,6 +1,12 @@ import NotFound from '@/views/NotFoundPage.vue' const routes = [ + { + path: '/', + redirect: (to) => { + return { path: '/login' } + }, + }, { path: '/overview', component: () => import('../views/KontoOverview.vue'), @@ -11,18 +17,30 @@ const routes = [ { path: '/profile', component: () => import('../views/Pages/UserProfileCard.vue'), + meta: { + requiresAuth: true, + }, }, { path: '/profileedit', component: () => import('../views/Pages/UserProfileEdit.vue'), + meta: { + requiresAuth: true, + }, }, { path: '/activity', component: () => import('../views/Pages/UserProfileActivity.vue'), + meta: { + requiresAuth: true, + }, }, { path: '/transactions', component: () => import('../views/Pages/UserProfileTransactionList.vue'), + meta: { + requiresAuth: true, + }, }, { path: '/login', @@ -40,11 +58,6 @@ const routes = [ path: '/password', component: () => import('../views/Pages/Password.vue'), }, - { - path: '/explorer', - name: 'Explorer', - component: () => import('../views/Pages/Explorer.vue'), - }, { path: '*', component: NotFound }, ] diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 96b2c4be8..d9128cfc6 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -1,11 +1,17 @@ import Vue from 'vue' import Vuex from 'vuex' +//import router from '../routes/router.js' Vue.use(Vuex) -import router from '../routes/router.js' import loginAPI from '../apis/loginAPI' import communityAPI from '../apis/communityAPI' +import createPersistedState from 'vuex-persistedstate' export const store = new Vuex.Store({ + plugins: [ + createPersistedState({ + storage: window.sessionStorage, + }), + ], state: { session_id: null, email: '', @@ -31,7 +37,6 @@ export const store = new Vuex.Store({ // Syncronous mutation of the state mutations: { language: (state, language) => { - //console.log('mutation: language', language) state.language = language $cookies.set('gdd_lang', language) if (state.language == 'de') { @@ -43,52 +48,34 @@ export const store = new Vuex.Store({ } }, loginfail: (state, loginfail) => { - //console.log('mutation: email') state.loginfail = loginfail }, email: (state, email) => { - //console.log('mutation: email') state.email = email }, session_id: (state, session_id) => { - //console.log('mutation: session_id') state.session_id = session_id }, user_balance: (state, balance) => { - //console.log('mutation: user_balance') state.user.balance = balance / 10000 }, user_balance_gdt: (state, balance) => { - //console.log('mutation: user_balance_gdt') state.user.balance_gdt = balance / 10000 }, }, // Asyncronous actions - used for api calls actions: { login: async ({ dispatch, commit }, data) => { - const result = await loginAPI.login(data.email, data.password) - if (result.success) { - commit('session_id', result.result.data.session_id) - commit('email', data.email) - $cookies.set('gdd_session_id', result.result.data.session_id) - $cookies.set('gdd_u', data.email) - router.push('/overview') - } else { - // Register failed, we perform a logout - //alert('>>>>> FAIl LOGIN') - commit('loginfail', true) - - //dispatch('logout') - } - }, - passwordReset: async (data) => { - //console.log('<<<<<<<<<<< PASSWORT RESET TODO >>>>>>>>>>>', data.email) + commit('session_id', data.session_id) + commit('email', data.email) + // $cookies.set('gdd_session_id', result.result.data.session_id) + // $cookies.set('gdd_u', data.email) }, + passwordReset: async (data) => {}, schoepfen: async (data) => { // http://localhost/transaction-creations/ajaxCreate }, createUser: async ({ commit, dispatch }, data) => { - // console.log('action: createUser') const result = await loginAPI.create( data.email, data.first_name, @@ -100,26 +87,20 @@ export const store = new Vuex.Store({ commit('email', data.email) $cookies.set('gdd_session_id', result.result.data.session_id) $cookies.set('gdd_u', data.email) - router.push('/overview') + //router.push('/overview') } else { // Register failed, we perform a logout - // console.log('action createUser to logout start') dispatch('logout') } }, logout: async ({ commit, state }) => { - //console.log('action: logout') if (state.session_id) { const result = await loginAPI.logout(state.session_id) // The result can be error, but thats ok with us } - + sessionStorage.clear() commit('session_id', null) commit('email', null) - $cookies.remove('gdd_session_id') - $cookies.remove('gdd_u') - $cookies.remove('gdd_lang') - router.push('/Login') }, accountBalance: async ({ commit, dispatch, state }) => { const result = await communityAPI.balance($cookies.get('gdd_session_id')) diff --git a/frontend/src/views/Layout/DashboardNavbar.vue b/frontend/src/views/Layout/DashboardNavbar.vue index ff1fb8ebf..e17afe27e 100755 --- a/frontend/src/views/Layout/DashboardNavbar.vue +++ b/frontend/src/views/Layout/DashboardNavbar.vue @@ -116,8 +116,8 @@ export default { this.activeNotifications = false }, logout() { - //console.log("DashboardNavbar.vue user logout() : ") this.$store.dispatch('logout') + this.$router.push('/login') }, }, } diff --git a/frontend/src/views/Pages/Login.vue b/frontend/src/views/Pages/Login.vue index 52e117bbc..a1ab86401 100755 --- a/frontend/src/views/Pages/Login.vue +++ b/frontend/src/views/Pages/Login.vue @@ -95,6 +95,8 @@ + From 38a447f62d4dcee03f11b1c1790f8422a6221d84 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 20 Apr 2021 08:00:17 +0200 Subject: [PATCH 05/12] reset to router --- frontend/src/routes/routes.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/frontend/src/routes/routes.js b/frontend/src/routes/routes.js index d487ed05e..f70afa1b5 100755 --- a/frontend/src/routes/routes.js +++ b/frontend/src/routes/routes.js @@ -41,9 +41,8 @@ const routes = [ component: () => import('../views/Pages/ForgotPassword.vue'), }, { - path: '/explorer', - name: 'Explorer', - component: () => import('../views/Pages/Explorer.vue'), + path: '/reset', + component: () => import('../views/Pages/ResetPassword.vue'), }, { path: '*', component: NotFound }, ] From 7fe5119783b1b4418cbc13bc75110746f3429e26 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 20 Apr 2021 09:27:35 +0200 Subject: [PATCH 06/12] add specs --- .../src/views/Pages/ResetPassword.spec.js | 107 ++++++++++++++++++ frontend/src/views/Pages/ResetPassword.vue | 2 +- 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 frontend/src/views/Pages/ResetPassword.spec.js diff --git a/frontend/src/views/Pages/ResetPassword.spec.js b/frontend/src/views/Pages/ResetPassword.spec.js new file mode 100644 index 000000000..ebff0fc20 --- /dev/null +++ b/frontend/src/views/Pages/ResetPassword.spec.js @@ -0,0 +1,107 @@ +import { mount, RouterLinkStub } from '@vue/test-utils' +import Vuex from 'vuex' +import flushPromises from 'flush-promises' + +import ResetPassword from './ResetPassword' + +const localVue = global.localVue + +describe('ResetPassword', () => { + let wrapper + + let mocks = { + $i18n: { + locale: 'en', + }, + $t: jest.fn((t) => t), + } + + let state = { + // loginfail: false, + } + + let store = new Vuex.Store({ + state, + }) + + let stubs = { + RouterLink: RouterLinkStub, + } + + const Wrapper = () => { + return mount(ResetPassword, { localVue, mocks, store, stubs }) + } + + describe('mount', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders the Reset Password form', () => { + expect(wrapper.find('div.resetpwd-form').exists()).toBeTruthy() + }) + + //describe('Register header', () => { + // it('has a welcome message', () => { + // expect(wrapper.find('div.header').text()).toBe('site.signup.title site.signup.subtitle') + // }) + //}) + + //describe('links', () => { + // it('has a link "Back"', () => { + // expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual('back') + // }) + + // it('links to /login when clicking "Back"', () => { + // expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/login') + // }) + //}) + + //describe('Register form', () => { + // it('has a register form', () => { + // expect(wrapper.find('form').exists()).toBeTruthy() + // }) + + // it('has 3 text input fields', () => { + // expect(wrapper.findAll('input[type="text"]').length).toBe(3) + // }) + + // it('has 2 password input fields', () => { + // expect(wrapper.findAll('input[type="password"]').length).toBe(2) + // }) + + // it('has 1 checkbox input fields', () => { + // expect(wrapper.findAll('input[type="checkbox"]').length).toBe(1) + // }) + + // it('has no submit button when not completely filled', () => { + // expect(wrapper.find('button[type="submit"]').exists()).toBe(false) + // }) + + // it('shows a warning when no valid Email is entered', async () => { + // wrapper.findAll('input[type="text"]').at(2).setValue('no_valid@Email') + // await flushPromises() + // await expect(wrapper.find('.invalid-feedback').text()).toEqual( + // 'The Email field must be a valid email', + // ) + // }) + + // it('shows 4 warnings when no password is set', async () => { + // const passwords = wrapper.findAll('input[type="password"]') + // passwords.at(0).setValue('') + // passwords.at(1).setValue('') + // await flushPromises() + // await expect(wrapper.find('div.hints').text()).toContain( + // 'site.signup.lowercase', + // 'site.signup.uppercase', + // 'site.signup.minimum', + // 'site.signup.one_number', + // ) + // }) + + // //TODO test different invalid password combinations + //}) + + // TODO test submit button + }) +}) diff --git a/frontend/src/views/Pages/ResetPassword.vue b/frontend/src/views/Pages/ResetPassword.vue index 0923d57ba..145fba404 100644 --- a/frontend/src/views/Pages/ResetPassword.vue +++ b/frontend/src/views/Pages/ResetPassword.vue @@ -1,5 +1,5 @@