Merge pull request #4787 from Ocelot-Social-Community/4785-test-refactor-email-templates

test: 🍰 Test And Refactor E-Mail Templates
This commit is contained in:
Wolfgang Huß 2022-06-23 19:05:43 +02:00 committed by GitHub
commit 1ecf5f2965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 364 additions and 91 deletions

View File

@ -0,0 +1,5 @@
// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js`
export default {
NONCE_LENGTH: 5,
INVITE_CODE_LENGTH: 6,
}

View File

@ -537,7 +537,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
await Factory.build(
'inviteCode',
{
code: 'AAAAAA',
code: 'ABCDEF',
},
{
generatedBy: jennyRostock,

View File

@ -19,9 +19,9 @@ const defaultParams = {
}
const englishHint = 'English version below!'
export const signupTemplate = ({ email, nonce, inviteCode = null }) => {
export const signupTemplate = ({ email, variables: { nonce, inviteCode = null } }) => {
const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!`
// dev format example: http://localhost:3000/registration?method=invite-mail&email=wolle.huss%40pjannto.com&nonce=64853
// dev format example: http://localhost:3000/registration?method=invite-mail&email=huss%40pjannto.com&nonce=64853
const actionUrl = new URL('/registration', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('email', email)
actionUrl.searchParams.set('nonce', nonce)
@ -31,72 +31,60 @@ export const signupTemplate = ({ email, nonce, inviteCode = null }) => {
} else {
actionUrl.searchParams.set('method', 'invite-mail')
}
const renderParams = { ...defaultParams, englishHint, actionUrl, nonce, subject }
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ ...defaultParams, englishHint, actionUrl, nonce, subject },
{ content: templates.signup },
),
html: mustache.render(templates.layout, renderParams, { content: templates.signup }),
}
}
export const emailVerificationTemplate = ({ email, nonce, name }) => {
export const emailVerificationTemplate = ({ email, variables: { nonce, name } }) => {
const subject = 'Neue E-Mail Adresse | New E-Mail Address'
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('email', email)
actionUrl.searchParams.set('nonce', nonce)
const renderParams = { ...defaultParams, englishHint, actionUrl, name, nonce, subject }
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ ...defaultParams, englishHint, actionUrl, name, nonce, subject },
{ content: templates.emailVerification },
),
html: mustache.render(templates.layout, renderParams, { content: templates.emailVerification }),
}
}
export const resetPasswordTemplate = ({ email, nonce, name }) => {
export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) => {
const subject = 'Neues Passwort | Reset Password'
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI)
actionUrl.searchParams.set('nonce', nonce)
actionUrl.searchParams.set('email', email)
const renderParams = { ...defaultParams, englishHint, actionUrl, name, nonce, subject }
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ ...defaultParams, englishHint, actionUrl, name, nonce, subject },
{ content: templates.passwordReset },
),
html: mustache.render(templates.layout, renderParams, { content: templates.passwordReset }),
}
}
export const wrongAccountTemplate = ({ email }) => {
export const wrongAccountTemplate = ({ email, _variables = {} }) => {
const subject = 'Falsche Mailadresse? | Wrong E-mail?'
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI)
const renderParams = { ...defaultParams, englishHint, actionUrl }
return {
from,
to: email,
subject,
html: mustache.render(
templates.layout,
{ ...defaultParams, englishHint, actionUrl },
{ content: templates.wrongAccount },
),
html: mustache.render(templates.layout, renderParams, { content: templates.wrongAccount }),
}
}
export const notificationTemplate = ({ email, notification }) => {
export const notificationTemplate = ({ email, variables: { notification } }) => {
const actionUrl = new URL('/notifications', CONFIG.CLIENT_URI)
const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI)
const renderParams = { ...defaultParams, name: notification.to.name, settingsUrl, actionUrl }

View File

@ -0,0 +1,246 @@
import CONFIG from '../../../config'
import logosWebapp from '../../../config/logos.js'
import {
signupTemplate,
emailVerificationTemplate,
resetPasswordTemplate,
wrongAccountTemplate,
notificationTemplate,
} from './templateBuilder'
const englishHint = 'English version below!'
const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI)
const supportUrl = CONFIG.SUPPORT_URL.toString()
let actionUrl, name, settingsUrl
const signupTemplateData = () => ({
email: 'test@example.org',
variables: {
nonce: '12345',
inviteCode: 'AAAAAA',
},
})
const emailVerificationTemplateData = () => ({
email: 'test@example.org',
variables: {
nonce: '12345',
name: 'Mr Example',
},
})
const resetPasswordTemplateData = () => ({
email: 'test@example.org',
variables: {
nonce: '12345',
name: 'Mr Example',
},
})
const wrongAccountTemplateData = () => ({
email: 'test@example.org',
variables: {},
})
const notificationTemplateData = (locale) => ({
email: 'test@example.org',
variables: {
notification: {
to: { name: 'Mr Example', locale },
},
},
})
const textsStandard = [
{
templPropName: 'from',
isContaining: false,
text: CONFIG.EMAIL_DEFAULT_SENDER,
},
{
templPropName: 'to',
isContaining: false,
text: 'test@example.org',
},
// is contained in html
welcomeImageUrl.toString(),
CONFIG.ORGANIZATION_URL,
CONFIG.APPLICATION_NAME,
]
const testEmailData = (emailTemplate, templateBuilder, templateData, texts) => {
if (!emailTemplate) {
emailTemplate = templateBuilder(templateData)
}
texts.forEach((element) => {
if (typeof element === 'object') {
if (element.isContaining) {
expect(emailTemplate[element.templPropName]).toEqual(expect.stringContaining(element.text))
} else {
expect(emailTemplate[element.templPropName]).toEqual(element.text)
}
} else {
expect(emailTemplate.html).toEqual(expect.stringContaining(element))
}
})
return emailTemplate
}
// beforeAll(async () => {
// await cleanDatabase()
// })
// afterAll(async () => {
// await cleanDatabase()
// })
describe('templateBuilder', () => {
describe('signupTemplate', () => {
describe('multi language', () => {
it('e-mail is build with all data', () => {
const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!`
const actionUrl = new URL('/registration', CONFIG.CLIENT_URI).toString()
const theSignupTemplateData = signupTemplateData()
const enContent = "Thank you for joining our cause it's awesome to have you on board."
const deContent =
'Danke, dass Du dich angemeldet hast wir freuen uns, Dich dabei zu haben.'
testEmailData(null, signupTemplate, theSignupTemplateData, [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
englishHint,
actionUrl,
theSignupTemplateData.variables.nonce,
theSignupTemplateData.variables.inviteCode,
enContent,
deContent,
supportUrl,
])
})
})
})
describe('emailVerificationTemplate', () => {
describe('multi language', () => {
it('e-mail is build with all data', () => {
const subject = 'Neue E-Mail Adresse | New E-Mail Address'
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI).toString()
const theEmailVerificationTemplateData = emailVerificationTemplateData()
const enContent = 'So, you want to change your e-mail? No problem!'
const deContent = 'Du möchtest also deine E-Mail ändern? Kein Problem!'
testEmailData(null, emailVerificationTemplate, theEmailVerificationTemplateData, [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
englishHint,
actionUrl,
theEmailVerificationTemplateData.variables.nonce,
theEmailVerificationTemplateData.variables.name,
enContent,
deContent,
supportUrl,
])
})
})
})
describe('resetPasswordTemplate', () => {
describe('multi language', () => {
it('e-mail is build with all data', () => {
const subject = 'Neues Passwort | Reset Password'
const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI).toString()
const theResetPasswordTemplateData = resetPasswordTemplateData()
const enContent = 'So, you forgot your password? No problem!'
const deContent = 'Du hast also dein Passwort vergessen? Kein Problem!'
testEmailData(null, resetPasswordTemplate, theResetPasswordTemplateData, [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
englishHint,
actionUrl,
theResetPasswordTemplateData.variables.nonce,
theResetPasswordTemplateData.variables.name,
enContent,
deContent,
supportUrl,
])
})
})
})
describe('wrongAccountTemplate', () => {
describe('multi language', () => {
it('e-mail is build with all data', () => {
const subject = 'Falsche Mailadresse? | Wrong E-mail?'
const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI).toString()
const theWrongAccountTemplateData = wrongAccountTemplateData()
const enContent =
"You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address."
const deContent =
'Du hast bei uns ein neues Passwort angefordert leider haben wir aber keinen Account mit Deiner E-Mailadresse gefunden.'
testEmailData(null, wrongAccountTemplate, theWrongAccountTemplateData, [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
englishHint,
actionUrl,
enContent,
deContent,
supportUrl,
])
})
})
})
describe('notificationTemplate', () => {
beforeEach(() => {
actionUrl = new URL('/notifications', CONFIG.CLIENT_URI).toString()
name = notificationTemplateData('en').variables.notification.to.name
settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI)
})
describe('en', () => {
it('e-mail is build with all data', () => {
const subject = `${CONFIG.APPLICATION_NAME} Notification`
const content = 'You received at least one notification. Click on this button to view them:'
testEmailData(null, notificationTemplate, notificationTemplateData('en'), [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
actionUrl,
name,
content,
settingsUrl,
])
})
})
describe('de', () => {
it('e-mail is build with all data', async () => {
const subject = `${CONFIG.APPLICATION_NAME} Benachrichtigung`
const content = `Du hast mindestens eine Benachrichtigung erhalten. Klick auf diesen Button, um sie anzusehen:`
testEmailData(null, notificationTemplate, notificationTemplateData('de'), [
...textsStandard,
{
templPropName: 'subject',
isContaining: false,
text: subject,
},
actionUrl,
name,
content,
settingsUrl,
])
})
})
})
})

View File

@ -25,8 +25,7 @@
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo {{ name }},</h1>
<p style="margin: 0;">Du hast mindestens eine Benachrichtigung erhalten. Klick auf diesen Button,
um sie anzusehen:</p>
<p style="margin: 0;">Du hast mindestens eine Benachrichtigung erhalten. Klick auf diesen Button, um sie anzusehen:</p>
</td>
</tr>
<tr>

View File

@ -24,8 +24,8 @@
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hallo!</h1>
<p style="margin: 0;">Du hast bei uns ein neues Passwort angefordert leider haben wir aber keinen
Account mit Deiner E-Mailadresse gefunden. Kann es sein, dass Du mit einer anderen Adresse bei uns
<p style="margin: 0;">Du hast bei uns ein neues Passwort angefordert leider haben wir aber keinen Account mit Deiner E-Mailadresse gefunden.
Kann es sein, dass Du mit einer anderen Adresse bei uns
angemeldet bist?</p>
</td>
</tr>
@ -122,8 +122,8 @@
<h1
style="margin: 0 0 10px 0; font-family: Lato, sans-serif; font-size: 25px; line-height: 30px; color: #333333; font-weight: normal;">
Hello!</h1>
<p style="margin: 0;">You requested a password reset but unfortunately we couldn't find an account
associated with your e-mail address. Did you maybe use another one when you signed up?</p>
<p style="margin: 0;">You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address.
Did you maybe use another one when you signed up?</p>
</td>
</tr>
<tr>

View File

@ -11,9 +11,9 @@ const sendSignupMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo)
const { email, nonce } = response
if (inviteCode) {
await sendMail(signupTemplate({ email, nonce, inviteCode }))
await sendMail(signupTemplate({ email, variables: { nonce, inviteCode } }))
} else {
await sendMail(signupTemplate({ email, nonce }))
await sendMail(signupTemplate({ email, variables: { nonce } }))
}
delete response.nonce
return response
@ -23,14 +23,14 @@ const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo)
const { email } = args
const { email: userFound, nonce, name } = await resolve(root, args, context, resolveInfo)
const template = userFound ? resetPasswordTemplate : wrongAccountTemplate
await sendMail(template({ email, nonce, name }))
await sendMail(template({ email, variables: { nonce, name } }))
return true
}
const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo)
const { email, nonce, name } = response
await sendMail(emailVerificationTemplate({ email, nonce, name }))
await sendMail(emailVerificationTemplate({ email, variables: { nonce, name } }))
delete response.nonce
return response
}

View File

@ -44,7 +44,7 @@ const publishNotifications = async (context, promises) => {
sendMail(
notificationTemplate({
email: notificationsEmailAddresses[index].email,
notification: notificationAdded,
variables: { notification: notificationAdded },
}),
)
}

View File

@ -182,12 +182,12 @@ describe('authorization', () => {
beforeEach(async () => {
variables = {
email: 'some@email.org',
inviteCode: 'AAAAAA',
inviteCode: 'ABCDEF',
}
CONFIG.INVITE_REGISTRATION = false
CONFIG.PUBLIC_REGISTRATION = false
await Factory.build('inviteCode', {
code: 'AAAAAA',
code: 'ABCDEF',
})
})
@ -224,12 +224,12 @@ describe('authorization', () => {
beforeEach(async () => {
variables = {
email: 'some@email.org',
inviteCode: 'AAAAAA',
inviteCode: 'ABCDEF',
}
CONFIG.INVITE_REGISTRATION = false
CONFIG.PUBLIC_REGISTRATION = true
await Factory.build('inviteCode', {
code: 'AAAAAA',
code: 'ABCDEF',
})
})
@ -254,7 +254,7 @@ describe('authorization', () => {
CONFIG.INVITE_REGISTRATION = true
CONFIG.PUBLIC_REGISTRATION = false
await Factory.build('inviteCode', {
code: 'AAAAAA',
code: 'ABCDEF',
})
})
@ -262,7 +262,7 @@ describe('authorization', () => {
beforeEach(async () => {
variables = {
email: 'some@email.org',
inviteCode: 'AAAAAA',
inviteCode: 'ABCDEF',
}
authenticatedUser = null
})

View File

@ -195,7 +195,7 @@ describe('slugifyMiddleware', () => {
variables = {
...variables,
name: 'I am a user',
nonce: '123456',
nonce: '12345',
password: 'yo',
email: '123@example.org',
termsAndConditionsAgreedVersion: '0.0.1',
@ -206,7 +206,7 @@ describe('slugifyMiddleware', () => {
beforeEach(async () => {
await Factory.build('emailAddress', {
email: '123@example.org',
nonce: '123456',
nonce: '12345',
verifiedAt: null,
})
})

View File

@ -158,7 +158,7 @@ describe('VerifyEmailAddress', () => {
`
beforeEach(() => {
variables = { ...variables, email: 'to-be-verified@example.org', nonce: '123456' }
variables = { ...variables, email: 'to-be-verified@example.org', nonce: '12345' }
})
describe('unauthenticated', () => {

View File

@ -1,8 +1,13 @@
import CONSTANTS_REGISTRATION from './../../../constants/registration'
export default function generateInviteCode() {
// 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z])
return Array.from({ length: 6 }, (n = Math.floor(Math.random() * 36)) => {
// n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65
// else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48
return String.fromCharCode(n > 9 ? n + 55 : n + 48)
}).join('')
return Array.from(
{ length: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH },
(n = Math.floor(Math.random() * 36)) => {
// n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65
// else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48
return String.fromCharCode(n > 9 ? n + 55 : n + 48)
},
).join('')
}

View File

@ -1,5 +1,11 @@
import CONSTANTS_REGISTRATION from './../../../constants/registration'
// TODO: why this is not used in resolver 'requestPasswordReset'?
export default function generateNonce() {
return Array.from({ length: 5 }, (n = Math.floor(Math.random() * 10)) => {
return String.fromCharCode(n + 48)
}).join('')
return Array.from(
{ length: CONSTANTS_REGISTRATION.NONCE_LENGTH },
(n = Math.floor(Math.random() * 10)) => {
return String.fromCharCode(n + 48)
},
).join('')
}

View File

@ -3,6 +3,7 @@ import { getDriver } from '../../db/neo4j'
import { gql } from '../../helpers/jest'
import createServer from '../../server'
import { createTestClient } from 'apollo-server-testing'
import CONSTANTS_REGISTRATION from './../../constants/registration'
let user
let query
@ -107,7 +108,11 @@ describe('inviteCodes', () => {
errors: undefined,
data: {
GenerateInviteCode: {
code: expect.stringMatching(/^[0-9A-Z]{6,6}$/),
code: expect.stringMatching(
new RegExp(
`^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`,
),
),
expiresAt: null,
createdAt: expect.any(String),
},
@ -129,7 +134,11 @@ describe('inviteCodes', () => {
errors: undefined,
data: {
GenerateInviteCode: {
code: expect.stringMatching(/^[0-9A-Z]{6,6}$/),
code: expect.stringMatching(
new RegExp(
`^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`,
),
),
expiresAt: nextWeek.toISOString(),
createdAt: expect.any(String),
},

View File

@ -1,11 +1,13 @@
import { v4 as uuid } from 'uuid'
import bcrypt from 'bcryptjs'
import CONSTANTS_REGISTRATION from './../../constants/registration'
import createPasswordReset from './helpers/createPasswordReset'
export default {
Mutation: {
requestPasswordReset: async (_parent, { email }, { driver }) => {
const nonce = uuid().substring(0, 6)
// TODO: why this is generated differntly from 'backend/src/schema/resolvers/helpers/generateNonce.js'?
const nonce = uuid().substring(0, CONSTANTS_REGISTRATION.NONCE_LENGTH)
return createPasswordReset({ driver, nonce, email })
},
resetPassword: async (_parent, { email, nonce, newPassword }, { driver }) => {

View File

@ -1,6 +1,7 @@
import Factory, { cleanDatabase } from '../../db/factories'
import { gql } from '../../helpers/jest'
import { getNeode, getDriver } from '../../db/neo4j'
import CONSTANTS_REGISTRATION from './../../constants/registration'
import createPasswordReset from './helpers/createPasswordReset'
import createServer from '../../server'
import { createTestClient } from 'apollo-server-testing'
@ -109,7 +110,7 @@ describe('passwordReset', () => {
const resets = await getAllPasswordResets()
const [reset] = resets
const { nonce } = reset.properties
expect(nonce).toHaveLength(6)
expect(nonce).toHaveLength(CONSTANTS_REGISTRATION.NONCE_LENGTH)
})
})
})
@ -118,7 +119,7 @@ describe('passwordReset', () => {
describe('resetPassword', () => {
const setup = async (options = {}) => {
const { email = 'user@example.org', issuedAt = new Date(), nonce = 'abcdef' } = options
const { email = 'user@example.org', issuedAt = new Date(), nonce = '12345' } = options
await createPasswordReset({ driver, email, issuedAt, nonce })
}
@ -148,7 +149,7 @@ describe('resetPassword', () => {
describe('invalid email', () => {
it('resolves to false', async () => {
await setup()
variables = { ...variables, email: 'non-existent@example.org', nonce: 'abcdef' }
variables = { ...variables, email: 'non-existent@example.org', nonce: '12345' }
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { resetPassword: false },
})
@ -162,7 +163,7 @@ describe('resetPassword', () => {
describe('but invalid nonce', () => {
beforeEach(() => {
variables = { ...variables, nonce: 'slkdjf' }
variables = { ...variables, nonce: 'slkdj' }
})
it('resolves to false', async () => {
@ -177,7 +178,7 @@ describe('resetPassword', () => {
beforeEach(() => {
variables = {
...variables,
nonce: 'abcdef',
nonce: '12345',
}
})

View File

@ -179,7 +179,7 @@ describe('SignupVerification', () => {
beforeEach(async () => {
variables = {
...variables,
nonce: '123456',
nonce: '12345',
name: 'John Doe',
password: '123',
email: 'john@example.org',
@ -207,7 +207,7 @@ describe('SignupVerification', () => {
describe('sending a valid nonce', () => {
beforeEach(() => {
variables = { ...variables, nonce: '123456' }
variables = { ...variables, nonce: '12345' }
})
it('rejects', async () => {
@ -222,7 +222,7 @@ describe('SignupVerification', () => {
beforeEach(async () => {
const args = {
email: 'john@example.org',
nonce: '123456',
nonce: '12345',
}
await neode.model('EmailAddress').create(args)
})

View File

@ -40,7 +40,7 @@ describe('ChangePassword ', () => {
describe('given email and nonce', () => {
beforeEach(() => {
propsData.email = 'mail@example.org'
propsData.nonce = '123456'
propsData.nonce = '12345'
})
describe('submitting new password', () => {
@ -57,7 +57,7 @@ describe('ChangePassword ', () => {
it('delivers new password to backend', () => {
const expected = expect.objectContaining({
variables: { nonce: '123456', email: 'mail@example.org', password: 'supersecret' },
variables: { nonce: '12345', email: 'mail@example.org', password: 'supersecret' },
})
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected)
})

View File

@ -22,6 +22,7 @@
<script>
import gql from 'graphql-tag'
import CONSTANTS_REGISTRATION from './../../constants/registration'
export const isValidInviteCodeQuery = gql`
query($code: ID!) {
@ -41,10 +42,12 @@ export default {
formSchema: {
inviteCode: {
type: 'string',
min: 6,
max: 6,
min: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH,
max: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH,
required: true,
message: this.$t('components.registration.invite-code.form.validations.length'),
message: this.$t('components.registration.invite-code.form.validations.length', {
inviteCodeLength: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH,
}),
},
},
dbRequestInProgress: false,

View File

@ -25,6 +25,8 @@
<script>
import gql from 'graphql-tag'
import { isEmail } from 'validator'
import CONSTANTS_REGISTRATION from './../../constants/registration'
import EmailDisplayAndVerify from './EmailDisplayAndVerify'
export const verifyNonceQuery = gql`
@ -48,10 +50,12 @@ export default {
formSchema: {
nonce: {
type: 'string',
min: 5,
max: 5,
min: CONSTANTS_REGISTRATION.NONCE_LENGTH,
max: CONSTANTS_REGISTRATION.NONCE_LENGTH,
required: true,
message: this.$t('components.registration.email-nonce.form.validations.length'),
message: this.$t('components.registration.email-nonce.form.validations.length', {
nonceLength: CONSTANTS_REGISTRATION.NONCE_LENGTH,
}),
},
},
dbRequestInProgress: false,

View File

@ -0,0 +1,5 @@
// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js`
export default {
NONCE_LENGTH: 5,
INVITE_CODE_LENGTH: 6,
}

View File

@ -170,7 +170,7 @@
"nonce": "E-Mail-Code: 32143",
"validations": {
"error": "Ungültiger Bestätigungs-Code <b>{nonce}</b> für E-Mail <b>{email}</b>!",
"length": "muss genau 5 Buchstaben lang sein",
"length": "muss genau {nonceLength} Buchstaben lang sein",
"success": "Gültiger Bestätigungs-Code <b>{nonce}</b> für E-Mail <b>{email}</b>!"
}
},
@ -184,7 +184,7 @@
"next": "Weiter",
"validations": {
"error": "Ungültiger Einladungs-Code <b>{inviteCode}</b>!",
"length": "muss genau 6 Buchstaben lang sein",
"length": "muss genau {inviteCodeLength} Buchstaben lang sein",
"success": "Gültiger Einladungs-Code <b>{inviteCode}</b>!"
}
}

View File

@ -170,7 +170,7 @@
"nonce": "E-mail code: 32143",
"validations": {
"error": "Invalid verification code <b>{nonce}</b> for e-mail <b>{email}</b>!",
"length": "must be 5 characters long",
"length": "must be {nonceLength} characters long",
"success": "Valid verification code <b>{nonce}</b> for e-mail <b>{email}</b>!"
}
},
@ -184,7 +184,7 @@
"next": "Continue",
"validations": {
"error": "Invalid invite code <b>{inviteCode}</b>!",
"length": "must be 6 characters long",
"length": "must be {inviteCodeLength} characters long",
"success": "Valid invite code <b>{inviteCode}</b>!"
}
}

View File

@ -143,7 +143,7 @@
"next": "Continuar",
"nonce": "Introduzca el código",
"validations": {
"length": "debe tener exactamente 5 letras"
"length": "debe tener exactamente {nonceLength} letras"
}
}
},

View File

@ -143,7 +143,7 @@
"next": "Continuer",
"nonce": "Entrez votre code",
"validations": {
"length": "doit comporter 5 caractères"
"length": "doit comporter {nonceLength} caractères"
}
}
},

View File

@ -104,7 +104,7 @@
"next": "Kontynuuj",
"nonce": "Wprowadź swój kod",
"validations": {
"length": "musi mieć długość 5 znaków."
"length": "musi mieć długość {nonceLength} znaków."
}
}
}

View File

@ -190,7 +190,7 @@
"next": "Continue",
"nonce": "Digite seu código",
"validations": {
"length": "deve ter 5 caracteres"
"length": "deve ter {nonceLength} caracteres"
}
}
},

View File

@ -143,7 +143,7 @@
"next": "Продолжить",
"nonce": "Введите код",
"validations": {
"length": "длина должна быть 5 символов"
"length": "длина должна быть {nonceLength} символов"
}
}
},

View File

@ -123,8 +123,8 @@ describe('Registration', () => {
expect(wrapper.find('.hc-empty').exists()).toBe(true)
})
it('"inviteCode=AAAAAA" query in URI', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'AAAAAA' }
it('"inviteCode=ABCDEF" query in URI', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'ABCDEF' }
wrapper = await Wrapper()
expect(wrapper.find('.hc-empty').exists()).toBe(true)
})
@ -181,12 +181,12 @@ describe('Registration', () => {
expect(wrapper.find('.enter-invite').exists()).toBe(true)
})
it('"inviteCode=AAAAAA" query in URI have invite code in input', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'AAAAAA' }
it('"inviteCode=ABCDEF" query in URI have invite code in input', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'ABCDEF' }
wrapper = await Wrapper()
await Vue.nextTick()
const form = wrapper.find('.enter-invite')
expect(form.vm.formData.inviteCode).toEqual('AAAAAA')
expect(form.vm.formData.inviteCode).toEqual('ABCDEF')
})
})
})
@ -294,12 +294,12 @@ describe('Registration', () => {
expect(wrapper.find('.enter-invite').exists()).toBe(true)
})
it('"inviteCode=AAAAAA" query in URI have invite code in input', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'AAAAAA' }
it('"inviteCode=ABCDEF" query in URI have invite code in input', async () => {
mocks.$route.query = { method: 'invite-code', inviteCode: 'ABCDEF' }
wrapper = await Wrapper()
await Vue.nextTick()
const form = wrapper.find('.enter-invite')
expect(form.vm.formData.inviteCode).toEqual('AAAAAA')
expect(form.vm.formData.inviteCode).toEqual('ABCDEF')
})
})
})