DRY up password validations

This commit is contained in:
Robert Schäfer 2019-07-08 18:57:57 +02:00
parent 47a9d38790
commit 4207aee09d
8 changed files with 88 additions and 122 deletions

View File

@ -278,7 +278,7 @@ When("I fill the password form with:", table => {
.type(table["Your old password"]) .type(table["Your old password"])
.get("input[id=newPassword]") .get("input[id=newPassword]")
.type(table["Your new passsword"]) .type(table["Your new passsword"])
.get("input[id=confirmPassword]") .get("input[id=passwordConfirmation]")
.type(table["Confirm new password"]); .type(table["Confirm new password"]);
}); });

View File

@ -54,7 +54,7 @@ describe('ChangePassword.vue', () => {
describe('match', () => { describe('match', () => {
beforeEach(() => { beforeEach(() => {
wrapper.find('input#oldPassword').setValue('some secret') wrapper.find('input#oldPassword').setValue('some secret')
wrapper.find('input#newPassword').setValue('some secret') wrapper.find('input#password').setValue('some secret')
}) })
it('invalid', () => { it('invalid', () => {
@ -90,8 +90,8 @@ describe('ChangePassword.vue', () => {
describe('given valid input', () => { describe('given valid input', () => {
beforeEach(() => { beforeEach(() => {
wrapper.find('input#oldPassword').setValue('supersecret') wrapper.find('input#oldPassword').setValue('supersecret')
wrapper.find('input#newPassword').setValue('superdupersecret') wrapper.find('input#password').setValue('superdupersecret')
wrapper.find('input#confirmPassword').setValue('superdupersecret') wrapper.find('input#passwordConfirmation').setValue('superdupersecret')
}) })
describe('submit form', () => { describe('submit form', () => {
@ -109,8 +109,8 @@ describe('ChangePassword.vue', () => {
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
oldPassword: 'supersecret', oldPassword: 'supersecret',
newPassword: 'superdupersecret', password: 'superdupersecret',
confirmPassword: 'superdupersecret', passwordConfirmation: 'superdupersecret',
}, },
}), }),
) )
@ -135,8 +135,8 @@ describe('ChangePassword.vue', () => {
/* describe('mutation rejects', () => { /* describe('mutation rejects', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.find('input#oldPassword').setValue('supersecret') await wrapper.find('input#oldPassword').setValue('supersecret')
await wrapper.find('input#newPassword').setValue('supersecret') await wrapper.find('input#password').setValue('supersecret')
await wrapper.find('input#confirmPassword').setValue('supersecret') await wrapper.find('input#passwordConfirmation').setValue('supersecret')
}) })
it('displays error message', async () => { it('displays error message', async () => {

View File

@ -1,9 +1,5 @@
<template> <template>
<ds-form <ds-form v-model="formData" :schema="formSchema" @submit="handleSubmit">
v-model="formData"
:schema="formSchema"
@submit="handleSubmit"
>
<template slot-scope="{ errors }"> <template slot-scope="{ errors }">
<ds-input <ds-input
id="oldPassword" id="oldPassword"
@ -13,20 +9,20 @@
:label="$t('settings.security.change-password.label-old-password')" :label="$t('settings.security.change-password.label-old-password')"
/> />
<ds-input <ds-input
id="newPassword" id="password"
model="newPassword" model="password"
type="password" type="password"
autocomplete="off" autocomplete="off"
:label="$t('settings.security.change-password.label-new-password')" :label="$t('settings.security.change-password.label-new-password')"
/> />
<ds-input <ds-input
id="confirmPassword" id="passwordConfirmation"
model="confirmPassword" model="passwordConfirmation"
type="password" type="password"
autocomplete="off" autocomplete="off"
:label="$t('settings.security.change-password.label-new-password-confirm')" :label="$t('settings.security.change-password.label-new-password-confirm')"
/> />
<password-strength :password="formData.newPassword" /> <password-strength :password="formData.password" />
<ds-space margin-top="base"> <ds-space margin-top="base">
<ds-button :loading="loading" :disabled="errors" primary> <ds-button :loading="loading" :disabled="errors" primary>
{{ $t('settings.security.change-password.button') }} {{ $t('settings.security.change-password.button') }}
@ -39,6 +35,7 @@
<script> <script>
import gql from 'graphql-tag' import gql from 'graphql-tag'
import PasswordStrength from './Strength' import PasswordStrength from './Strength'
import PasswordForm from '~/components/utils/PasswordFormHelper'
export default { export default {
name: 'ChangePassword', name: 'ChangePassword',
@ -46,11 +43,11 @@ export default {
PasswordStrength, PasswordStrength,
}, },
data() { data() {
const passwordForm = PasswordForm({ translate: this.$t })
return { return {
formData: { formData: {
oldPassword: '', oldPassword: '',
newPassword: '', ...passwordForm.formData,
confirmPassword: '',
}, },
formSchema: { formSchema: {
oldPassword: { oldPassword: {
@ -58,21 +55,7 @@ export default {
required: true, required: true,
message: this.$t('settings.security.change-password.message-old-password-required'), message: this.$t('settings.security.change-password.message-old-password-required'),
}, },
newPassword: { ...passwordForm.formSchema,
type: 'string',
required: true,
message: this.$t('settings.security.change-password.message-new-password-required'),
},
confirmPassword: [
{ validator: this.matchPassword },
{
type: 'string',
required: true,
message: this.$t(
'settings.security.change-password.message-new-password-confirm-required',
),
},
],
}, },
loading: false, loading: false,
disabled: true, disabled: true,
@ -82,8 +65,8 @@ export default {
async handleSubmit(data) { async handleSubmit(data) {
this.loading = true this.loading = true
const mutation = gql` const mutation = gql`
mutation($oldPassword: String!, $newPassword: String!) { mutation($oldPassword: String!, $password: String!) {
changePassword(oldPassword: $oldPassword, newPassword: $newPassword) changePassword(oldPassword: $oldPassword, newPassword: $password)
} }
` `
const variables = this.formData const variables = this.formData
@ -94,8 +77,8 @@ export default {
this.$toast.success(this.$t('settings.security.change-password.success')) this.$toast.success(this.$t('settings.security.change-password.success'))
this.formData = { this.formData = {
oldPassword: '', oldPassword: '',
newPassword: '', password: '',
confirmPassword: '', passwordConfirmation: '',
} }
} catch (err) { } catch (err) {
this.$toast.error(err.message) this.$toast.error(err.message)
@ -103,15 +86,6 @@ export default {
this.loading = false this.loading = false
} }
}, },
matchPassword(rule, value, callback, source, options) {
var errors = []
if (this.formData.newPassword !== value) {
errors.push(
new Error(this.$t('settings.security.change-password.message-new-password-missmatch')),
)
}
callback(errors)
},
}, },
} }
</script> </script>

View File

@ -47,8 +47,8 @@ describe('ChangePassword ', () => {
describe('submitting new password', () => { describe('submitting new password', () => {
beforeEach(() => { beforeEach(() => {
wrapper = Wrapper() wrapper = Wrapper()
wrapper.find('input#newPassword').setValue('supersecret') wrapper.find('input#password').setValue('supersecret')
wrapper.find('input#confirmPassword').setValue('supersecret') wrapper.find('input#passwordConfirmation').setValue('supersecret')
wrapper.find('form').trigger('submit') wrapper.find('form').trigger('submit')
}) })
@ -58,7 +58,7 @@ describe('ChangePassword ', () => {
it('delivers new password to backend', () => { it('delivers new password to backend', () => {
const expected = expect.objectContaining({ const expected = expect.objectContaining({
variables: { code: '123456', email: 'mail@example.org', newPassword: 'supersecret' }, variables: { code: '123456', email: 'mail@example.org', password: 'supersecret' },
}) })
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
}) })

View File

@ -10,20 +10,20 @@
> >
<template slot-scope="{ errors }"> <template slot-scope="{ errors }">
<ds-input <ds-input
id="newPassword" id="password"
model="newPassword" model="password"
type="password" type="password"
autocomplete="off" autocomplete="off"
:label="$t('settings.security.change-password.label-new-password')" :label="$t('settings.security.change-password.label-new-password')"
/> />
<ds-input <ds-input
id="confirmPassword" id="passwordConfirmation"
model="confirmPassword" model="passwordConfirmation"
type="password" type="password"
autocomplete="off" autocomplete="off"
:label="$t('settings.security.change-password.label-new-password-confirm')" :label="$t('settings.security.change-password.label-new-password-confirm')"
/> />
<password-strength :password="formData.newPassword" /> <password-strength :password="formData.password" />
<ds-space margin-top="base"> <ds-space margin-top="base">
<ds-button :loading="$apollo.loading" :disabled="errors" primary> <ds-button :loading="$apollo.loading" :disabled="errors" primary>
{{ $t('settings.security.change-password.button') }} {{ $t('settings.security.change-password.button') }}
@ -55,6 +55,7 @@
import PasswordStrength from '../Password/Strength' import PasswordStrength from '../Password/Strength'
import gql from 'graphql-tag' import gql from 'graphql-tag'
import { SweetalertIcon } from 'vue-sweetalert-icons' import { SweetalertIcon } from 'vue-sweetalert-icons'
import PasswordForm from '~/components/utils/PasswordFormHelper'
export default { export default {
components: { components: {
@ -66,27 +67,13 @@ export default {
code: { type: String, required: true }, code: { type: String, required: true },
}, },
data() { data() {
const passwordForm = PasswordForm({ translate: this.$t })
return { return {
formData: { formData: {
newPassword: '', ...passwordForm.formData,
confirmPassword: '',
}, },
formSchema: { formSchema: {
newPassword: { ...passwordForm.formSchema,
type: 'string',
required: true,
message: this.$t('settings.security.change-password.message-new-password-required'),
},
confirmPassword: [
{ validator: this.matchPassword },
{
type: 'string',
required: true,
message: this.$t(
'settings.security.change-password.message-new-password-confirm-required',
),
},
],
}, },
disabled: true, disabled: true,
changePasswordResult: null, changePasswordResult: null,
@ -95,13 +82,13 @@ export default {
methods: { methods: {
async handleSubmitPassword() { async handleSubmitPassword() {
const mutation = gql` const mutation = gql`
mutation($code: String!, $email: String!, $newPassword: String!) { mutation($code: String!, $email: String!, $password: String!) {
resetPassword(code: $code, email: $email, newPassword: $newPassword) resetPassword(code: $code, email: $email, newPassword: $password)
} }
` `
const { newPassword } = this.formData const { password } = this.formData
const { email, code } = this const { email, code } = this
const variables = { newPassword, email, code } const variables = { password, email, code }
try { try {
const { const {
data: { resetPassword }, data: { resetPassword },
@ -111,22 +98,13 @@ export default {
this.$emit('passwordResetResponse', this.changePasswordResult) this.$emit('passwordResetResponse', this.changePasswordResult)
}, 3000) }, 3000)
this.formData = { this.formData = {
newPassword: '', password: '',
confirmPassword: '', passwordConfirmation: '',
} }
} catch (err) { } catch (err) {
this.$toast.error(err.message) this.$toast.error(err.message)
} }
}, },
matchPassword(rule, value, callback, source, options) {
var errors = []
if (this.formData.newPassword !== value) {
errors.push(
new Error(this.$t('settings.security.change-password.message-new-password-missmatch')),
)
}
callback(errors)
},
}, },
} }
</script> </script>

View File

@ -54,7 +54,7 @@ describe('CreateUserAccount', () => {
wrapper = Wrapper() wrapper = Wrapper()
wrapper.find('input#name').setValue('John Doe') wrapper.find('input#name').setValue('John Doe')
wrapper.find('input#password').setValue('hellopassword') wrapper.find('input#password').setValue('hellopassword')
wrapper.find('input#confirmPassword').setValue('hellopassword') wrapper.find('input#passwordConfirmation').setValue('hellopassword')
await wrapper.find('form').trigger('submit') await wrapper.find('form').trigger('submit')
await wrapper.html() await wrapper.html()
} }
@ -123,7 +123,7 @@ describe('CreateUserAccount', () => {
it('displays form errors', async () => { it('displays form errors', async () => {
await action() await action()
expect(wrapper.find('.errors').text()).toContain('Invalid nonce') expect(wrapper.find('.backendErrors').text()).toContain('Invalid nonce')
}) })
}) })
}) })

View File

@ -39,17 +39,17 @@
:label="$t('settings.security.change-password.label-new-password')" :label="$t('settings.security.change-password.label-new-password')"
/> />
<ds-input <ds-input
id="confirmPassword" id="passwordConfirmation"
model="confirmPassword" model="passwordConfirmation"
type="password" type="password"
autocomplete="off" autocomplete="off"
:label="$t('settings.security.change-password.label-new-password-confirm')" :label="$t('settings.security.change-password.label-new-password-confirm')"
/> />
<password-strength :password="formData.password" /> <password-strength :password="formData.password" />
<template slot="footer"> <template slot="footer">
<ds-space class="errors" v-if="errors"> <ds-space class="backendErrors" v-if="backendErrors">
<ds-text align="center" bold color="danger"> <ds-text align="center" bold color="danger">
{{ errors.message }} {{ backendErrors.message }}
</ds-text> </ds-text>
</ds-space> </ds-space>
<ds-button <ds-button
@ -72,6 +72,7 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
import PasswordStrength from '../Password/Strength' import PasswordStrength from '../Password/Strength'
import { SweetalertIcon } from 'vue-sweetalert-icons' import { SweetalertIcon } from 'vue-sweetalert-icons'
import PasswordForm from '~/components/utils/PasswordFormHelper'
export const SignupVerificationMutation = gql` export const SignupVerificationMutation = gql`
mutation($nonce: String!, $name: String!, $email: String!, $password: String!) { mutation($nonce: String!, $name: String!, $email: String!, $password: String!) {
@ -88,12 +89,12 @@ export default {
SweetalertIcon, SweetalertIcon,
}, },
data() { data() {
const passwordForm = PasswordForm({ translate: this.$t })
return { return {
formData: { formData: {
name: '', name: '',
about: '', about: '',
password: '', ...passwordForm.formData,
confirmPassword: '',
}, },
formSchema: { formSchema: {
name: { name: {
@ -105,25 +106,11 @@ export default {
type: 'string', type: 'string',
required: false, required: false,
}, },
password: { ...passwordForm.formSchema,
type: 'string',
required: true,
message: this.$t('settings.security.change-password.message-new-password-required'),
},
confirmPassword: [
{ validator: this.matchPassword },
{
type: 'string',
required: true,
message: this.$t(
'settings.security.change-password.message-new-password-confirm-required',
),
},
],
}, },
disabled: true, disabled: true,
success: null, success: null,
errors: null, backendErrors: null,
} }
}, },
props: { props: {
@ -147,18 +134,9 @@ export default {
}) })
}, 3000) }, 3000)
} catch (err) { } catch (err) {
this.errors = err this.backendErrors = err
} }
}, },
matchPassword(rule, value, callback, source, options) {
var errors = []
if (this.formData.password !== value) {
errors.push(
new Error(this.$t('settings.security.change-password.message-new-password-missmatch')),
)
}
callback(errors)
},
}, },
} }
</script> </script>

View File

@ -0,0 +1,36 @@
export default function PasswordForm({ translate }) {
const passwordMismatchMessage = translate(
'settings.security.change-password.message-new-password-missmatch',
)
return {
formData: {
password: '',
passwordConfirmation: '',
},
formSchema: {
password: {
type: 'string',
required: true,
message: translate('settings.security.change-password.message-new-password-required'),
},
passwordConfirmation: [
{
validator(rule, value, callback, source, options) {
var errors = []
if (source.password !== value) {
errors.push(new Error(passwordMismatchMessage))
}
callback(errors)
},
},
{
type: 'string',
required: true,
message: translate(
'settings.security.change-password.message-new-password-confirm-required',
),
},
],
},
}
}