mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #2984 from gradido/feat-username-in-wallet
feat(frontend): username in wallet
This commit is contained in:
commit
e9edf1a712
@ -8,4 +8,5 @@ export const INALIENABLE_RIGHTS = [
|
||||
RIGHTS.SET_PASSWORD,
|
||||
RIGHTS.QUERY_TRANSACTION_LINK,
|
||||
RIGHTS.QUERY_OPT_IN,
|
||||
RIGHTS.CHECK_USERNAME,
|
||||
]
|
||||
|
||||
@ -34,6 +34,7 @@ export enum RIGHTS {
|
||||
LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES',
|
||||
OPEN_CREATIONS = 'OPEN_CREATIONS',
|
||||
USER = 'USER',
|
||||
CHECK_USERNAME = 'CHECK_USERNAME',
|
||||
// Admin
|
||||
SEARCH_USERS = 'SEARCH_USERS',
|
||||
SET_USER_ROLE = 'SET_USER_ROLE',
|
||||
|
||||
@ -53,6 +53,7 @@ import {
|
||||
searchAdminUsers,
|
||||
searchUsers,
|
||||
user as userQuery,
|
||||
checkUsername,
|
||||
} from '@/seeds/graphql/queries'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
||||
@ -2442,6 +2443,34 @@ describe('UserResolver', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('check username', () => {
|
||||
describe('reserved alias', () => {
|
||||
it('returns false', async () => {
|
||||
await expect(
|
||||
query({ query: checkUsername, variables: { username: 'root' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
checkUsername: false,
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid alias', () => {
|
||||
it('returns true', async () => {
|
||||
await expect(
|
||||
query({ query: checkUsername, variables: { username: 'valid' } }),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
checkUsername: true,
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('printTimeDuration', () => {
|
||||
|
||||
@ -498,6 +498,17 @@ export class UserResolver {
|
||||
return true
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.CHECK_USERNAME])
|
||||
@Query(() => Boolean)
|
||||
async checkUsername(@Arg('username') username: string): Promise<boolean> {
|
||||
try {
|
||||
await validateAlias(username)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.UPDATE_USER_INFOS])
|
||||
@Mutation(() => Boolean)
|
||||
async updateUserInfos(
|
||||
|
||||
@ -22,6 +22,12 @@ export const queryOptIn = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const checkUsername = gql`
|
||||
query ($username: String!) {
|
||||
checkUsername(username: $username)
|
||||
}
|
||||
`
|
||||
|
||||
export const transactionsQuery = gql`
|
||||
query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
|
||||
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
containsLowercaseCharacter: true,
|
||||
containsUppercaseCharacter: true,
|
||||
containsNumericCharacter: true,
|
||||
atLeastEightCharactera: true,
|
||||
atLeastEightCharacters: true,
|
||||
atLeastOneSpecialCharater: true,
|
||||
noWhitespaceCharacters: true,
|
||||
}"
|
||||
|
||||
71
frontend/src/components/Inputs/InputUsername.vue
Normal file
71
frontend/src/components/Inputs/InputUsername.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<validation-provider
|
||||
tag="div"
|
||||
:rules="rules"
|
||||
:name="name"
|
||||
:bails="!showAllErrors"
|
||||
:immediate="immediate"
|
||||
vid="username"
|
||||
v-slot="{ errors, valid, validated, ariaInput, ariaMsg }"
|
||||
>
|
||||
<b-form-group :label-for="labelFor">
|
||||
<b-form-input
|
||||
v-model="currentValue"
|
||||
v-bind="ariaInput"
|
||||
:id="labelFor"
|
||||
:name="name"
|
||||
:placeholder="placeholder"
|
||||
type="text"
|
||||
:state="validated ? valid : false"
|
||||
autocomplete="off"
|
||||
></b-form-input>
|
||||
<b-form-invalid-feedback v-bind="ariaMsg">
|
||||
<div v-if="showAllErrors">
|
||||
<span v-for="error in errors" :key="error">
|
||||
{{ error }}
|
||||
<br />
|
||||
</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ errors[0] }}
|
||||
</div>
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
</validation-provider>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'InputUsername',
|
||||
props: {
|
||||
rules: {
|
||||
default: () => {
|
||||
return {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
name: { type: String, default: 'username' },
|
||||
label: { type: String, default: 'Username' },
|
||||
placeholder: { type: String, default: 'Username' },
|
||||
value: { required: true, type: String },
|
||||
showAllErrors: { type: Boolean, default: false },
|
||||
immediate: { type: Boolean, default: false },
|
||||
unique: { type: Boolean, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
labelFor() {
|
||||
return this.name + '-input-field'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
currentValue() {
|
||||
this.$emit('input', this.currentValue)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
157
frontend/src/components/UserSettings/UserName.spec.js
Normal file
157
frontend/src/components/UserSettings/UserName.spec.js
Normal file
@ -0,0 +1,157 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import UserName from './UserName'
|
||||
import flushPromises from 'flush-promises'
|
||||
|
||||
import { toastErrorSpy, toastSuccessSpy } from '@test/testSetup'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const mockAPIcall = jest.fn()
|
||||
|
||||
const storeCommitMock = jest.fn()
|
||||
|
||||
describe('UserName Form', () => {
|
||||
let wrapper
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$store: {
|
||||
state: {
|
||||
username: 'peter',
|
||||
},
|
||||
commit: storeCommitMock,
|
||||
},
|
||||
$apollo: {
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
const Wrapper = () => {
|
||||
return mount(UserName, { localVue, mocks })
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('renders the component', () => {
|
||||
expect(wrapper.find('div#username_form').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has an edit icon', () => {
|
||||
expect(wrapper.find('svg.bi-pencil').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('renders the username', () => {
|
||||
expect(wrapper.findAll('div.col').at(2).text()).toBe('peter')
|
||||
})
|
||||
|
||||
describe('edit username', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.find('svg.bi-pencil').trigger('click')
|
||||
})
|
||||
|
||||
it('shows an cancel icon', () => {
|
||||
expect(wrapper.find('svg.bi-x-circle').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('closes the input when cancel icon is clicked', async () => {
|
||||
await wrapper.find('svg.bi-x-circle').trigger('click')
|
||||
expect(wrapper.find('input').exists()).toBeFalsy()
|
||||
})
|
||||
|
||||
it('does not change the username when cancel is clicked', async () => {
|
||||
await wrapper.find('input').setValue('petra')
|
||||
await wrapper.find('svg.bi-x-circle').trigger('click')
|
||||
expect(wrapper.findAll('div.col').at(2).text()).toBe('peter')
|
||||
})
|
||||
|
||||
it('has a submit button', () => {
|
||||
expect(wrapper.find('button[type="submit"]').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('does not enable submit button when data is not changed', async () => {
|
||||
await wrapper.find('form').trigger('keyup')
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
describe('successfull submit', () => {
|
||||
beforeEach(async () => {
|
||||
mockAPIcall.mockResolvedValue({
|
||||
data: {
|
||||
updateUserInfos: {
|
||||
validValues: 3,
|
||||
},
|
||||
},
|
||||
})
|
||||
jest.clearAllMocks()
|
||||
await wrapper.find('input').setValue('petra')
|
||||
await wrapper.find('form').trigger('keyup')
|
||||
await wrapper.find('button[type="submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(mockAPIcall).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
alias: 'petra',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('commits username to store', () => {
|
||||
expect(storeCommitMock).toBeCalledWith('username', 'petra')
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('settings.username.change-success')
|
||||
})
|
||||
|
||||
it('has an edit button again', () => {
|
||||
expect(wrapper.find('svg.bi-pencil').exists()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('submit results in server error', () => {
|
||||
beforeEach(async () => {
|
||||
mockAPIcall.mockRejectedValue({
|
||||
message: 'Error',
|
||||
})
|
||||
jest.clearAllMocks()
|
||||
await wrapper.find('input').setValue('petra')
|
||||
await wrapper.find('form').trigger('keyup')
|
||||
await wrapper.find('button[type="submit"]').trigger('click')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(mockAPIcall).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
alias: 'petra',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('no username in store', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$store.state.username = null
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('displays an information why to enter a username', () => {
|
||||
expect(wrapper.findAll('div.col').at(2).text()).toBe('settings.username.no-username')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
130
frontend/src/components/UserSettings/UserName.vue
Normal file
130
frontend/src/components/UserSettings/UserName.vue
Normal file
@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<b-card id="username_form" class="card-border-radius card-background-gray">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a
|
||||
class="cursor-pointer"
|
||||
@click="showUserData ? (showUserData = !showUserData) : cancelEdit()"
|
||||
>
|
||||
<span class="pointer mr-3">{{ $t('settings.username.change-username') }}</span>
|
||||
<b-icon v-if="showUserData" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<validation-observer ref="usernameObserver" v-slot="{ handleSubmit, invalid }">
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-row class="mb-3">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
<b>{{ $t('form.username') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col v-if="showUserData" class="col-12">
|
||||
<span v-if="username">
|
||||
{{ username }}
|
||||
</span>
|
||||
<div v-else class="alert">
|
||||
{{ $t('settings.username.no-username') }}
|
||||
</div>
|
||||
</b-col>
|
||||
<b-col v-else class="col-12">
|
||||
<input-username
|
||||
v-model="username"
|
||||
:name="$t('form.username')"
|
||||
:placeholder="$t('form.username-placeholder')"
|
||||
:showAllErrors="true"
|
||||
:unique="true"
|
||||
:rules="rules"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="text-right" v-if="!showUserData">
|
||||
<b-col>
|
||||
<div class="text-right" ref="submitButton">
|
||||
<b-button
|
||||
:variant="disabled(invalid) ? 'light' : 'success'"
|
||||
@click="onSubmit"
|
||||
type="submit"
|
||||
:disabled="disabled(invalid)"
|
||||
>
|
||||
{{ $t('form.save') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</b-form>
|
||||
</validation-observer>
|
||||
</div>
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { updateUserInfos } from '@/graphql/mutations'
|
||||
import InputUsername from '@/components/Inputs/InputUsername'
|
||||
|
||||
export default {
|
||||
name: 'UserName',
|
||||
components: {
|
||||
InputUsername,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showUserData: true,
|
||||
username: this.$store.state.username || '',
|
||||
usernameUnique: false,
|
||||
rules: {
|
||||
required: true,
|
||||
min: 3,
|
||||
max: 20,
|
||||
usernameAllowedChars: true,
|
||||
usernameHyphens: true,
|
||||
usernameUnique: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
cancelEdit() {
|
||||
this.username = this.$store.state.username || ''
|
||||
this.showUserData = true
|
||||
},
|
||||
async onSubmit(event) {
|
||||
event.preventDefault()
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
alias: this.username,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.commit('username', this.username)
|
||||
this.showUserData = true
|
||||
this.toastSuccess(this.$t('settings.username.change-success'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.toastError(error.message)
|
||||
})
|
||||
},
|
||||
disabled(invalid) {
|
||||
return !this.newUsername || invalid
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
newUsername() {
|
||||
return this.username !== this.$store.state.username
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
div.alert {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
@ -26,6 +26,7 @@ export const forgotPassword = gql`
|
||||
|
||||
export const updateUserInfos = gql`
|
||||
mutation(
|
||||
$alias: String
|
||||
$firstName: String
|
||||
$lastName: String
|
||||
$password: String
|
||||
@ -35,6 +36,7 @@ export const updateUserInfos = gql`
|
||||
$hideAmountGDT: Boolean
|
||||
) {
|
||||
updateUserInfos(
|
||||
alias: $alias
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
password: $password
|
||||
@ -145,6 +147,7 @@ export const login = gql`
|
||||
mutation($email: String!, $password: String!, $publisherId: Int) {
|
||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||
gradidoID
|
||||
alias
|
||||
firstName
|
||||
lastName
|
||||
language
|
||||
|
||||
@ -89,6 +89,12 @@ export const queryOptIn = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const checkUsername = gql`
|
||||
query($username: String!) {
|
||||
checkUsername(username: $username)
|
||||
}
|
||||
`
|
||||
|
||||
export const queryTransactionLink = gql`
|
||||
query($code: String!) {
|
||||
queryTransactionLink(code: $code) {
|
||||
|
||||
@ -167,12 +167,15 @@
|
||||
"thx": "Danke",
|
||||
"to": "bis",
|
||||
"to1": "an",
|
||||
"username": "Nutzername",
|
||||
"username-placeholder": "Gebe einen eindeutigen Nutzernamen ein",
|
||||
"validation": {
|
||||
"gddCreationTime": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens einer Nachkommastelle sein",
|
||||
"gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein",
|
||||
"is-not": "Du kannst dir selbst keine Gradidos überweisen",
|
||||
"usernmae-regex": "Der Username muss mit einem Buchstaben beginnen, auf den mindestens zwei alpha-numerische Zeichen folgen müssen.",
|
||||
"usernmae-unique": "Der Username ist bereits vergeben."
|
||||
"username-allowed-chars": "Der Nutzername darf nur aus Buchstaben (ohne Umlaute), Zahlen, Binde- oder Unterstrichen bestehen.",
|
||||
"username-hyphens": "Binde- oder Unterstriche müssen zwischen Buchstaben oder Zahlen stehen.",
|
||||
"username-unique": "Der Nutzername ist bereits vergeben."
|
||||
},
|
||||
"your_amount": "Dein Betrag"
|
||||
},
|
||||
@ -320,7 +323,12 @@
|
||||
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen."
|
||||
},
|
||||
"showAmountGDD": "Dein GDD Betrag ist sichtbar.",
|
||||
"showAmountGDT": "Dein GDT Betrag ist sichtbar."
|
||||
"showAmountGDT": "Dein GDT Betrag ist sichtbar.",
|
||||
"username": {
|
||||
"change-success": "Dein Nutzername wurde erfolgreich geändert.",
|
||||
"change-username": "Nutzername ändern",
|
||||
"no-username": "Bitte gebe einen Nutzernamen ein. Damit hilfst du anderen Benutzern dich zu finden, ohne deine Email preisgeben zu müssen."
|
||||
}
|
||||
},
|
||||
"signin": "Anmelden",
|
||||
"signup": "Registrieren",
|
||||
|
||||
@ -167,12 +167,15 @@
|
||||
"thx": "Thank you",
|
||||
"to": "to",
|
||||
"to1": "to",
|
||||
"username": "Username",
|
||||
"username-placeholder": "Enter a unique username",
|
||||
"validation": {
|
||||
"gddCreationTime": "The field {_field_} must be a number between {min} and {max} with at most one decimal place.",
|
||||
"gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits after the decimal point",
|
||||
"is-not": "You cannot send Gradidos to yourself",
|
||||
"usernmae-regex": "The username must start with a letter, followed by at least two alphanumeric characters.",
|
||||
"usernmae-unique": "This username is already taken."
|
||||
"username-allowed-chars": "The username may only contain letters, numbers, hyphens or underscores.",
|
||||
"username-hyphens": "Hyphens or underscores must be in between letters or numbers.",
|
||||
"username-unique": "This username is already taken."
|
||||
},
|
||||
"your_amount": "Your amount"
|
||||
},
|
||||
@ -320,7 +323,12 @@
|
||||
"subtitle": "If you have forgotten your password, you can reset it here."
|
||||
},
|
||||
"showAmountGDD": "Your GDD amount is visible.",
|
||||
"showAmountGDT": "Your GDT amount is visible."
|
||||
"showAmountGDT": "Your GDT amount is visible.",
|
||||
"username": {
|
||||
"change-success": "Your username has been changed successfully.",
|
||||
"change-username": "Change username",
|
||||
"no-username": "Please enter a username. This helps other users to find you without exposing your email."
|
||||
}
|
||||
},
|
||||
"signin": "Sign in",
|
||||
"signup": "Sign up",
|
||||
|
||||
@ -27,7 +27,7 @@ const filters = loadFilters(i18n)
|
||||
Vue.filter('amount', filters.amount)
|
||||
Vue.filter('GDD', filters.GDD)
|
||||
|
||||
loadAllRules(i18n)
|
||||
loadAllRules(i18n, apolloProvider.defaultClient)
|
||||
|
||||
addNavigationGuards(router, store, apolloProvider.defaultClient)
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
<user-card :balance="balance" :transactionCount="transactionCount"></user-card>
|
||||
<user-data />
|
||||
<hr />
|
||||
<user-name />
|
||||
<hr />
|
||||
<user-password />
|
||||
<hr />
|
||||
<user-language />
|
||||
@ -13,6 +15,7 @@
|
||||
<script>
|
||||
import UserCard from '@/components/UserSettings/UserCard'
|
||||
import UserData from '@/components/UserSettings/UserData'
|
||||
import UserName from '@/components/UserSettings/UserName'
|
||||
import UserPassword from '@/components/UserSettings/UserPassword'
|
||||
import UserLanguage from '@/components/UserSettings/UserLanguage'
|
||||
import UserNewsletter from '@/components/UserSettings/UserNewsletter'
|
||||
@ -22,6 +25,7 @@ export default {
|
||||
components: {
|
||||
UserCard,
|
||||
UserData,
|
||||
UserName,
|
||||
UserPassword,
|
||||
UserLanguage,
|
||||
UserNewsletter,
|
||||
|
||||
@ -16,9 +16,9 @@ export const mutations = {
|
||||
gradidoID: (state, gradidoID) => {
|
||||
state.gradidoID = gradidoID
|
||||
},
|
||||
// username: (state, username) => {
|
||||
// state.username = username
|
||||
// },
|
||||
username: (state, username) => {
|
||||
state.username = username
|
||||
},
|
||||
firstName: (state, firstName) => {
|
||||
state.firstName = firstName
|
||||
},
|
||||
@ -59,7 +59,7 @@ export const actions = {
|
||||
login: ({ dispatch, commit }, data) => {
|
||||
commit('gradidoID', data.gradidoID)
|
||||
commit('language', data.language)
|
||||
// commit('username', data.username)
|
||||
commit('username', data.alias)
|
||||
commit('firstName', data.firstName)
|
||||
commit('lastName', data.lastName)
|
||||
commit('newsletterState', data.klickTipp.newsletterState)
|
||||
@ -71,7 +71,7 @@ export const actions = {
|
||||
},
|
||||
logout: ({ commit, state }) => {
|
||||
commit('token', null)
|
||||
// commit('username', '')
|
||||
commit('username', '')
|
||||
commit('gradidoID', null)
|
||||
commit('firstName', '')
|
||||
commit('lastName', '')
|
||||
|
||||
@ -26,6 +26,7 @@ const {
|
||||
token,
|
||||
firstName,
|
||||
lastName,
|
||||
username,
|
||||
newsletterState,
|
||||
publisherId,
|
||||
isAdmin,
|
||||
@ -104,6 +105,14 @@ describe('Vuex store', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('username', () => {
|
||||
it('sets the state of username', () => {
|
||||
const state = { username: null }
|
||||
username(state, 'peter')
|
||||
expect(state.username).toEqual('peter')
|
||||
})
|
||||
})
|
||||
|
||||
describe('newsletterState', () => {
|
||||
it('sets the state of newsletterState', () => {
|
||||
const state = { newsletterState: null }
|
||||
@ -166,6 +175,7 @@ describe('Vuex store', () => {
|
||||
const commitedData = {
|
||||
gradidoID: 'my-gradido-id',
|
||||
language: 'de',
|
||||
alias: 'peter',
|
||||
firstName: 'Peter',
|
||||
lastName: 'Lustig',
|
||||
klickTipp: {
|
||||
@ -180,7 +190,7 @@ describe('Vuex store', () => {
|
||||
|
||||
it('calls eleven commits', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenCalledTimes(10)
|
||||
expect(commit).toHaveBeenCalledTimes(11)
|
||||
})
|
||||
|
||||
it('commits gradidoID', () => {
|
||||
@ -193,44 +203,49 @@ describe('Vuex store', () => {
|
||||
expect(commit).toHaveBeenNthCalledWith(2, 'language', 'de')
|
||||
})
|
||||
|
||||
it('commits username', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(3, 'username', 'peter')
|
||||
})
|
||||
|
||||
it('commits firstName', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(3, 'firstName', 'Peter')
|
||||
expect(commit).toHaveBeenNthCalledWith(4, 'firstName', 'Peter')
|
||||
})
|
||||
|
||||
it('commits lastName', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(4, 'lastName', 'Lustig')
|
||||
expect(commit).toHaveBeenNthCalledWith(5, 'lastName', 'Lustig')
|
||||
})
|
||||
|
||||
it('commits newsletterState', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(5, 'newsletterState', true)
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'newsletterState', true)
|
||||
})
|
||||
|
||||
it('commits hasElopage', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'hasElopage', false)
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'hasElopage', false)
|
||||
})
|
||||
|
||||
it('commits publisherId', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'publisherId', 1234)
|
||||
expect(commit).toHaveBeenNthCalledWith(8, 'publisherId', 1234)
|
||||
})
|
||||
|
||||
it('commits isAdmin', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', true)
|
||||
expect(commit).toHaveBeenNthCalledWith(9, 'isAdmin', true)
|
||||
})
|
||||
|
||||
it('commits hideAmountGDD', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(9, 'hideAmountGDD', false)
|
||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDD', false)
|
||||
})
|
||||
|
||||
it('commits hideAmountGDT', () => {
|
||||
login({ commit, state }, commitedData)
|
||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDT', true)
|
||||
expect(commit).toHaveBeenNthCalledWith(11, 'hideAmountGDT', true)
|
||||
})
|
||||
})
|
||||
|
||||
@ -240,7 +255,7 @@ describe('Vuex store', () => {
|
||||
|
||||
it('calls eleven commits', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenCalledTimes(10)
|
||||
expect(commit).toHaveBeenCalledTimes(11)
|
||||
})
|
||||
|
||||
it('commits token', () => {
|
||||
@ -248,49 +263,54 @@ describe('Vuex store', () => {
|
||||
expect(commit).toHaveBeenNthCalledWith(1, 'token', null)
|
||||
})
|
||||
|
||||
it('commits username', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(2, 'username', '')
|
||||
})
|
||||
|
||||
it('commits gradidoID', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(2, 'gradidoID', null)
|
||||
expect(commit).toHaveBeenNthCalledWith(3, 'gradidoID', null)
|
||||
})
|
||||
|
||||
it('commits firstName', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(3, 'firstName', '')
|
||||
expect(commit).toHaveBeenNthCalledWith(4, 'firstName', '')
|
||||
})
|
||||
|
||||
it('commits lastName', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(4, 'lastName', '')
|
||||
expect(commit).toHaveBeenNthCalledWith(5, 'lastName', '')
|
||||
})
|
||||
|
||||
it('commits newsletterState', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(5, 'newsletterState', null)
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'newsletterState', null)
|
||||
})
|
||||
|
||||
it('commits hasElopage', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(6, 'hasElopage', false)
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'hasElopage', false)
|
||||
})
|
||||
|
||||
it('commits publisherId', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(7, 'publisherId', null)
|
||||
expect(commit).toHaveBeenNthCalledWith(8, 'publisherId', null)
|
||||
})
|
||||
|
||||
it('commits isAdmin', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', false)
|
||||
expect(commit).toHaveBeenNthCalledWith(9, 'isAdmin', false)
|
||||
})
|
||||
|
||||
it('commits hideAmountGDD', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(9, 'hideAmountGDD', false)
|
||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDD', false)
|
||||
})
|
||||
|
||||
it('commits hideAmountGDT', () => {
|
||||
logout({ commit, state })
|
||||
expect(commit).toHaveBeenNthCalledWith(10, 'hideAmountGDT', true)
|
||||
expect(commit).toHaveBeenNthCalledWith(11, 'hideAmountGDT', true)
|
||||
})
|
||||
// how to get this working?
|
||||
it.skip('calls localStorage.clear()', () => {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { configure, extend } from 'vee-validate'
|
||||
// eslint-disable-next-line camelcase
|
||||
import { required, email, min, max, is_not } from 'vee-validate/dist/rules'
|
||||
import { checkUsername } from '@/graphql/queries'
|
||||
|
||||
export const loadAllRules = (i18nCallback) => {
|
||||
export const loadAllRules = (i18nCallback, apollo) => {
|
||||
configure({
|
||||
defaultMessage: (field, values) => {
|
||||
// eslint-disable-next-line @intlify/vue-i18n/no-dynamic-keys
|
||||
@ -96,7 +97,7 @@ export const loadAllRules = (i18nCallback) => {
|
||||
message: (_, values) => i18nCallback.t('site.signup.one_number', values),
|
||||
})
|
||||
|
||||
extend('atLeastEightCharactera', {
|
||||
extend('atLeastEightCharacters', {
|
||||
validate(value) {
|
||||
return !!value.match(/.{8,}/)
|
||||
},
|
||||
@ -123,4 +124,35 @@ export const loadAllRules = (i18nCallback) => {
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('site.signup.dont_match', values),
|
||||
})
|
||||
|
||||
extend('usernameAllowedChars', {
|
||||
validate(value) {
|
||||
return !!value.match(/^[a-zA-Z0-9_-]+$/)
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('form.validation.username-allowed-chars', values),
|
||||
})
|
||||
|
||||
extend('usernameHyphens', {
|
||||
validate(value) {
|
||||
return !!value.match(/^[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/)
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('form.validation.username-hyphens', values),
|
||||
})
|
||||
|
||||
extend('usernameUnique', {
|
||||
validate(value) {
|
||||
if (!value.match(/^(?=.{3,20}$)[a-zA-Z0-9]+(?:[_-][a-zA-Z0-9]+?)*$/)) return true
|
||||
return apollo
|
||||
.query({
|
||||
query: checkUsername,
|
||||
variables: { username: value },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
return {
|
||||
valid: data.checkUsername,
|
||||
}
|
||||
})
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('form.validation.username-unique', values),
|
||||
})
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@ const i18nMock = {
|
||||
n: (value, format) => value,
|
||||
}
|
||||
|
||||
loadAllRules(i18nMock)
|
||||
loadAllRules(i18nMock, { query: jest.fn().mockResolvedValue({ data: { checkUsername: true } }) })
|
||||
|
||||
global.localVue = createLocalVue()
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user