From 4625e0d10bfebd48375e99b261fb9facaa65ddc7 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 1 Jul 2021 04:47:56 +0200 Subject: [PATCH] refactor: Change Password --- .../src/components/Inputs/InputPassword.vue | 14 +- frontend/src/main.js | 71 +----- frontend/src/validation-rules.js | 104 ++++++++ .../UserCard_FormUserPasswort.spec.js | 84 ++++-- .../UserProfile/UserCard_FormUserPasswort.vue | 241 ++++++------------ 5 files changed, 255 insertions(+), 259 deletions(-) create mode 100644 frontend/src/validation-rules.js diff --git a/frontend/src/components/Inputs/InputPassword.vue b/frontend/src/components/Inputs/InputPassword.vue index e4fdd34b4..6b72f0b01 100644 --- a/frontend/src/components/Inputs/InputPassword.vue +++ b/frontend/src/components/Inputs/InputPassword.vue @@ -3,6 +3,8 @@ tag="div" :rules="rules" :name="name" + :bails="!showAllErrors" + :immediate="immediate" v-slot="{ errors, valid, validated, ariaInput, ariaMsg }" > @@ -22,7 +24,15 @@ - {{ errors[0] }} +
+ + {{ error }} +
+
+
+
+ {{ errors[0] }} +
@@ -43,6 +53,8 @@ export default { label: { type: String, default: 'Password' }, placeholder: { type: String, default: 'Password' }, value: { required: true, type: String }, + showAllErrors: { type: Boolean, default: false }, + immediate: { type: Boolean, default: false }, }, data() { return { diff --git a/frontend/src/main.js b/frontend/src/main.js index 1eda3eaca..731fc896a 100755 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -2,16 +2,11 @@ import Vue from 'vue' import DashboardPlugin from './plugins/dashboard-plugin' import App from './App.vue' import i18n from './i18n.js' -import { configure, extend } from 'vee-validate' -// eslint-disable-next-line camelcase -import { required, email, min, max, is_not } from 'vee-validate/dist/rules' +// eslint-disable-next-line no-unused-vars +import validationRules from './validation-rules' -// store import { store } from './store/store' -import loginAPI from './apis/loginAPI' - -// router setup import router from './routes/router' // plugin setup @@ -26,68 +21,6 @@ router.beforeEach((to, from, next) => { } }) -configure({ - defaultMessage: (field, values) => { - values._field_ = i18n.t(`fields.${field}`) - return i18n.t(`validations.messages.${values._rule_}`, values) - }, -}) - -extend('email', { - ...email, - message: (_, values) => i18n.t('validations.messages.email', values), -}) - -extend('required', { - ...required, - message: (_, values) => i18n.t('validations.messages.required', values), -}) - -extend('min', { - ...min, - message: (_, values) => i18n.t('validations.messages.min', values), -}) - -extend('max', { - ...max, - message: (_, values) => i18n.t('validations.messages.max', values), -}) - -extend('gddSendAmount', { - validate(value, { min, max }) { - value = value.replace(',', '.') - return value.match(/^[0-9]+(\.[0-9]{0,2})?$/) && Number(value) >= min && Number(value) <= max - }, - params: ['min', 'max'], - message: (_, values) => { - values.min = i18n.n(values.min, 'ungroupedDecimal') - values.max = i18n.n(values.max, 'ungroupedDecimal') - return i18n.t('form.validation.gddSendAmount', values) - }, -}) - -extend('gddUsernameUnique', { - async validate(value) { - const result = await loginAPI.checkUsername(value) - return result.result.data.state === 'success' - }, - message: (_, values) => i18n.t('form.validation.usernmae-unique', values), -}) - -extend('gddUsernameRgex', { - validate(value) { - return !!value.match(/^[a-zA-Z][-_a-zA-Z0-9]{2,}$/) - }, - message: (_, values) => i18n.t('form.validation.usernmae-regex', values), -}) - -// eslint-disable-next-line camelcase -extend('is_not', { - // eslint-disable-next-line camelcase - ...is_not, - message: (_, values) => i18n.t('form.validation.is-not', values), -}) - /* eslint-disable no-new */ new Vue({ el: '#app', diff --git a/frontend/src/validation-rules.js b/frontend/src/validation-rules.js new file mode 100644 index 000000000..e92e820bb --- /dev/null +++ b/frontend/src/validation-rules.js @@ -0,0 +1,104 @@ +import i18n from './i18n.js' +import { configure, extend } from 'vee-validate' +// eslint-disable-next-line camelcase +import { required, email, min, max, is_not } from 'vee-validate/dist/rules' +import loginAPI from './apis/loginAPI' + +configure({ + defaultMessage: (field, values) => { + values._field_ = i18n.t(`fields.${field}`) + return i18n.t(`validations.messages.${values._rule_}`, values) + }, +}) + +extend('email', { + ...email, + message: (_, values) => i18n.t('validations.messages.email', values), +}) + +extend('required', { + ...required, + message: (_, values) => i18n.t('validations.messages.required', values), +}) + +extend('min', { + ...min, + message: (_, values) => i18n.t('validations.messages.min', values), +}) + +extend('max', { + ...max, + message: (_, values) => i18n.t('validations.messages.max', values), +}) + +extend('gddSendAmount', { + validate(value, { min, max }) { + value = value.replace(',', '.') + return value.match(/^[0-9]+(\.[0-9]{0,2})?$/) && Number(value) >= min && Number(value) <= max + }, + params: ['min', 'max'], + message: (_, values) => { + values.min = i18n.n(values.min, 'ungroupedDecimal') + values.max = i18n.n(values.max, 'ungroupedDecimal') + return i18n.t('form.validation.gddSendAmount', values) + }, +}) + +extend('gddUsernameUnique', { + async validate(value) { + const result = await loginAPI.checkUsername(value) + return result.result.data.state === 'success' + }, + message: (_, values) => i18n.t('form.validation.usernmae-unique', values), +}) + +extend('gddUsernameRgex', { + validate(value) { + return !!value.match(/^[a-zA-Z][-_a-zA-Z0-9]{2,}$/) + }, + message: (_, values) => i18n.t('form.validation.usernmae-regex', values), +}) + +// eslint-disable-next-line camelcase +extend('is_not', { + // eslint-disable-next-line camelcase + ...is_not, + message: (_, values) => i18n.t('form.validation.is-not', values), +}) + +// Password validation + +extend('containsLowercaseCharacter', { + validate(value) { + return !!value.match(/[a-z]+/) + }, + message: (_, values) => i18n.t('site.signup.lowercase', values), +}) + +extend('containsUppercaseCharacter', { + validate(value) { + return !!value.match(/[A-Z]+/) + }, + message: (_, values) => i18n.t('site.signup.uppercase', values), +}) + +extend('containsNumericCharacter', { + validate(value) { + return !!value.match(/[0-9]+/) + }, + message: (_, values) => i18n.t('site.signup.one_number', values), +}) + +extend('atLeastEightCharactera', { + validate(value) { + return !!value.match(/.{8,}/) + }, + message: (_, values) => i18n.t('site.signup.minimum', values), +}) + +extend('samePassword', { + validate(value, [pwd]) { + return value === pwd + }, + message: (_, values) => i18n.t('site.signup.dont_match', values), +}) diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js index 4461464a6..fb435cedb 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.spec.js @@ -1,13 +1,32 @@ import { mount } from '@vue/test-utils' import UserCardFormPasswort from './UserCard_FormUserPasswort' import loginAPI from '../../../apis/loginAPI' -// import flushPromises from 'flush-promises' +import flushPromises from 'flush-promises' +import { extend } from 'vee-validate' + +const rules = [ + 'containsLowercaseCharacter', + 'containsUppercaseCharacter', + 'containsNumericCharacter', + 'atLeastEightCharactera', + 'samePassword', +] + +rules.forEach((rule) => { + extend(rule, { + validate(value) { + return true + }, + }) +}) jest.mock('../../../apis/loginAPI') const localVue = global.localVue const changePasswordProfileMock = jest.fn() +changePasswordProfileMock.mockReturnValue({ success: true }) + loginAPI.changePasswordProfile = changePasswordProfileMock const toastSuccessMock = jest.fn() @@ -59,8 +78,8 @@ describe('UserCardFormUserPasswort', () => { let form beforeEach(async () => { - wrapper.find('a').trigger('click') - await wrapper.vm.$nextTick() + await wrapper.find('a').trigger('click') + await flushPromises() form = wrapper.find('form') }) @@ -69,12 +88,11 @@ describe('UserCardFormUserPasswort', () => { }) it('has a cancel button', () => { - expect(form.find('svg.bi-x-circle').exists()).toBeTruthy() + expect(wrapper.find('svg.bi-x-circle').exists()).toBeTruthy() }) it('closes the form when cancel button is clicked', async () => { - form.find('svg.bi-x-circle').trigger('click') - await wrapper.vm.$nextTick() + await wrapper.find('svg.bi-x-circle').trigger('click') expect(wrapper.find('input').exists()).toBeFalsy() }) @@ -104,24 +122,52 @@ describe('UserCardFormUserPasswort', () => { expect(form.find('button[type="submit"]').exists()).toBeTruthy() }) - /* describe('submit', () => { - beforeEach(async () => { - await form.findAll('input').at(0).setValue('1234') - await form.findAll('input').at(1).setValue('Aa123456') - await form.findAll('input').at(2).setValue('Aa123456') - form.trigger('submit') - await wrapper.vm.$nextTick() - await flushPromises() + describe('valid data', () => { + beforeEach(async () => { + await form.findAll('input').at(0).setValue('1234') + await form.findAll('input').at(1).setValue('Aa123456') + await form.findAll('input').at(2).setValue('Aa123456') + await form.trigger('submit') + await flushPromises() + }) + + it('calls the API', () => { + expect(changePasswordProfileMock).toHaveBeenCalledWith( + 1, + 'user@example.org', + '1234', + 'Aa123456', + ) + }) + + it('toasts a success message', () => { + expect(toastSuccessMock).toBeCalledWith('site.thx.reset') + }) + + it('cancels the edit process', () => { + expect(wrapper.find('input').exists()).toBeFalsy() + }) }) - it('calls the API', async () => { - await wrapper.vm.$nextTick() - await flushPromises() -expect(changePasswordProfileMock).toHaveBeenCalledWith(1, 'user@example.org', '1234', 'Aa123456') + describe('server response is error', () => { + beforeEach(async () => { + changePasswordProfileMock.mockReturnValue({ + success: false, + result: { message: 'error' }, + }) + await form.findAll('input').at(0).setValue('1234') + await form.findAll('input').at(1).setValue('Aa123456') + await form.findAll('input').at(2).setValue('Aa123456') + await form.trigger('submit') + await flushPromises() + }) + + it('toasts an error message', () => { + expect(toastErrorMock).toBeCalledWith('error') + }) }) }) - */ }) }) }) diff --git a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue index 269c6c7be..ba49662fd 100644 --- a/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue +++ b/frontend/src/views/Pages/UserProfile/UserCard_FormUserPasswort.vue @@ -1,183 +1,110 @@ -