merge with master

This commit is contained in:
Einhornimmond 2021-10-10 15:44:11 +02:00
commit f6cba24c00
38 changed files with 1850 additions and 1508 deletions

View File

@ -346,7 +346,7 @@ jobs:
report_name: Coverage Frontend report_name: Coverage Frontend
type: lcov type: lcov
result_path: ./coverage/lcov.info result_path: ./coverage/lcov.info
min_coverage: 73 min_coverage: 76
token: ${{ github.token }} token: ${{ github.token }}
############################################################################## ##############################################################################

View File

@ -27,7 +27,7 @@ import { TransactionSignature as DbTransactionSignature } from '../../typeorm/en
import { TransactionSendCoin as DbTransactionSendCoin } from '../../typeorm/entity/TransactionSendCoin' import { TransactionSendCoin as DbTransactionSendCoin } from '../../typeorm/entity/TransactionSendCoin'
import { Balance as DbBalance } from '../../typeorm/entity/Balance' import { Balance as DbBalance } from '../../typeorm/entity/Balance'
import { apiGet, apiPost } from '../../apis/HttpRequest' import { apiPost } from '../../apis/HttpRequest'
import { roundFloorFrom4 } from '../../util/round' import { roundFloorFrom4 } from '../../util/round'
import { calculateDecay, calculateDecayWithInterval } from '../../util/decay' import { calculateDecay, calculateDecayWithInterval } from '../../util/decay'
import { TransactionTypeId } from '../enum/TransactionTypeId' import { TransactionTypeId } from '../enum/TransactionTypeId'
@ -579,13 +579,9 @@ export class TransactionResolver {
@Args() { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated, @Args() { currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
@Ctx() context: any, @Ctx() context: any,
): Promise<TransactionList> { ): Promise<TransactionList> {
// get public key for current logged in user
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
if (!result.success) throw new Error(result.data)
// load user // load user
const userRepository = getCustomRepository(UserRepository) const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(result.data.user.public_hex) const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
const transactions = await listTransactions(currentPage, pageSize, order, userEntity) const transactions = await listTransactions(currentPage, pageSize, order, userEntity)

View File

@ -2,12 +2,14 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql' import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
import { from_hex as fromHex } from 'libsodium-wrappers'
import CONFIG from '../../config' import CONFIG from '../../config'
import { CheckUsernameResponse } from '../model/CheckUsernameResponse' import { CheckUsernameResponse } from '../model/CheckUsernameResponse'
import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode' import { LoginViaVerificationCode } from '../model/LoginViaVerificationCode'
import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse' import { SendPasswordResetEmailResponse } from '../model/SendPasswordResetEmailResponse'
import { UpdateUserInfosResponse } from '../model/UpdateUserInfosResponse' import { UpdateUserInfosResponse } from '../model/UpdateUserInfosResponse'
import { User } from '../model/User' import { User } from '../model/User'
import { User as DbUser } from '../../typeorm/entity/User'
import encode from '../../jwt/encode' import encode from '../../jwt/encode'
import ChangePasswordArgs from '../arg/ChangePasswordArgs' import ChangePasswordArgs from '../arg/ChangePasswordArgs'
import CheckUsernameArgs from '../arg/CheckUsernameArgs' import CheckUsernameArgs from '../arg/CheckUsernameArgs'
@ -102,6 +104,18 @@ export class UserResolver {
throw new Error(result.data) throw new Error(result.data)
} }
const user = new User(result.data.user)
const dbuser = new DbUser()
dbuser.pubkey = Buffer.from(fromHex(user.pubkey))
dbuser.email = user.email
dbuser.firstName = user.firstName
dbuser.lastName = user.lastName
dbuser.username = user.username
dbuser.save().catch(() => {
throw new Error('error saving user')
})
return 'success' return 'success'
} }

View File

@ -17,6 +17,8 @@ async function calculateDecay(amount: number, from: Date, to: Date): Promise<num
// if decay hasn't started yet we return input amount // if decay hasn't started yet we return input amount
if (!decayStartBlock) return amount if (!decayStartBlock) return amount
// what happens when from > to
// Do we want to have negative decay?
const decayDuration = (to.getTime() - from.getTime()) / 1000 const decayDuration = (to.getTime() - from.getTime()) / 1000
return decayFormula(amount, decayDuration) return decayFormula(amount, decayDuration)
} }

File diff suppressed because it is too large Load Diff

View File

@ -122,7 +122,6 @@ describe('LanguageSwitch', () => {
expect(updateUserInfosMutationMock).toBeCalledWith( expect(updateUserInfosMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'he@ho.he',
locale: 'en', locale: 'en',
}, },
}), }),
@ -134,7 +133,6 @@ describe('LanguageSwitch', () => {
expect(updateUserInfosMutationMock).toBeCalledWith( expect(updateUserInfosMutationMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'he@ho.he',
locale: 'de', locale: 'de',
}, },
}), }),

View File

@ -39,7 +39,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
locale: locale, locale: locale,
}, },
}) })

View File

@ -6,6 +6,7 @@ import { loadAllRules } from './validation-rules'
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost' import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from 'apollo-boost'
import VueApollo from 'vue-apollo' import VueApollo from 'vue-apollo'
import CONFIG from './config' import CONFIG from './config'
import addNavigationGuards from './routes/guards'
import { store } from './store/store' import { store } from './store/store'
@ -49,13 +50,7 @@ Vue.config.productionTip = false
loadAllRules(i18n) loadAllRules(i18n)
router.beforeEach((to, from, next) => { addNavigationGuards(router, store)
if (to.meta.requiresAuth && !store.state.token) {
next({ path: '/login' })
} else {
next()
}
})
/* eslint-disable no-new */ /* eslint-disable no-new */
new Vue({ new Vue({

View File

@ -1,4 +1,4 @@
import clickOutside from '@/directives/click-ouside.js' // import clickOutside from '@/directives/click-ouside.js'
import { focus } from 'vue-focus' import { focus } from 'vue-focus'
/** /**
@ -7,7 +7,7 @@ import { focus } from 'vue-focus'
const GlobalDirectives = { const GlobalDirectives = {
install(Vue) { install(Vue) {
Vue.directive('click-outside', clickOutside) // Vue.directive('click-outside', clickOutside)
Vue.directive('focus', focus) Vue.directive('focus', focus)
}, },
} }

View File

@ -0,0 +1,18 @@
const addNavigationGuards = (router, store) => {
router.beforeEach((to, from, next) => {
// handle publisherId
const publisherId = to.query.pid
if (publisherId) {
store.commit('publisherId', publisherId)
delete to.query.pid
}
// handle authentication
if (to.meta.requiresAuth && !store.state.token) {
next({ path: '/login' })
} else {
next()
}
})
}
export default addNavigationGuards

View File

@ -0,0 +1,38 @@
import addNavigationGuards from './guards'
import router from './router'
const storeCommitMock = jest.fn()
const store = {
commit: storeCommitMock,
state: {
token: null,
},
}
addNavigationGuards(router, store)
describe('navigation guards', () => {
beforeEach(() => {
jest.clearAllMocks()
})
describe('publisher ID', () => {
it('commits the pid to the store when present', () => {
router.push({ path: 'login', query: { pid: 42 } })
expect(storeCommitMock).toBeCalledWith('publisherId', '42')
})
it('does not commit the pid when not present', () => {
router.push({ path: 'register' })
expect(storeCommitMock).not.toBeCalled()
})
})
describe('authorization', () => {
it.skip('redirects to login when not authorized', async () => {
router.push({ path: 'overview' })
expect(router.history.current.path).toBe('/login')
})
})
})

View File

@ -5,10 +5,9 @@ import CONFIG from '../config'
Vue.use(VueRouter) Vue.use(VueRouter)
// configure router
const router = new VueRouter({ const router = new VueRouter({
base: '/vue', base: '/vue',
routes, // short for routes: routes routes,
linkActiveClass: 'active', linkActiveClass: 'active',
mode: 'history', mode: 'history',
scrollBehavior: (to, from, savedPosition) => { scrollBehavior: (to, from, savedPosition) => {

View File

@ -55,8 +55,8 @@ describe('router', () => {
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' }) expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
}) })
it('has ten routes defined', () => { it('has twelve routes defined', () => {
expect(routes).toHaveLength(10) expect(routes).toHaveLength(12)
}) })
describe('overview', () => { describe('overview', () => {
@ -131,6 +131,20 @@ describe('router', () => {
}) })
}) })
describe('register-community', () => {
it('loads the "registerCommunity" component', async () => {
const component = await routes.find((r) => r.path === '/register-community').component()
expect(component.default.name).toBe('registerCommunity')
})
})
describe('select-community', () => {
it('loads the "registerSelectCommunity" component', async () => {
const component = await routes.find((r) => r.path === '/select-community').component()
expect(component.default.name).toBe('registerSelectCommunity')
})
})
describe('reset', () => { describe('reset', () => {
it('loads the "ResetPassword" component', async () => { it('loads the "ResetPassword" component', async () => {
const component = await routes.find((r) => r.path === '/reset/:optin').component() const component = await routes.find((r) => r.path === '/reset/:optin').component()

View File

@ -36,7 +36,7 @@ const routes = [
path: '/thx/:comingFrom', path: '/thx/:comingFrom',
component: () => import('../views/Pages/thx.vue'), component: () => import('../views/Pages/thx.vue'),
beforeEnter: (to, from, next) => { beforeEnter: (to, from, next) => {
const validFrom = ['password', 'reset', 'register', 'community'] const validFrom = ['password', 'reset', 'register']
if (!validFrom.includes(from.path.split('/')[1])) { if (!validFrom.includes(from.path.split('/')[1])) {
next({ path: '/login' }) next({ path: '/login' })
} else { } else {

View File

@ -29,9 +29,15 @@ export const mutations = {
newsletterState: (state, newsletterState) => { newsletterState: (state, newsletterState) => {
state.newsletterState = newsletterState state.newsletterState = newsletterState
}, },
publisherId: (state, publisherId) => {
state.publisherId = publisherId
},
community: (state, community) => { community: (state, community) => {
state.community = community state.community = community
}, },
coinanimation: (state, coinanimation) => {
state.coinanimation = coinanimation
},
} }
export const actions = { export const actions = {
@ -42,6 +48,7 @@ export const actions = {
commit('firstName', data.firstName) commit('firstName', data.firstName)
commit('lastName', data.lastName) commit('lastName', data.lastName)
commit('description', data.description) commit('description', data.description)
commit('coinanimation', data.coinanimation)
commit('newsletterState', data.klickTipp.newsletterState) commit('newsletterState', data.klickTipp.newsletterState)
}, },
logout: ({ commit, state }) => { logout: ({ commit, state }) => {
@ -51,6 +58,7 @@ export const actions = {
commit('firstName', '') commit('firstName', '')
commit('lastName', '') commit('lastName', '')
commit('description', '') commit('description', '')
commit('coinanimation', true)
commit('newsletterState', null) commit('newsletterState', null)
localStorage.clear() localStorage.clear()
}, },

View File

@ -8,7 +8,10 @@ const {
firstName, firstName,
lastName, lastName,
description, description,
coinanimation,
newsletterState, newsletterState,
publisherId,
community,
} = mutations } = mutations
const { login, logout } = actions const { login, logout } = actions
@ -70,6 +73,14 @@ describe('Vuex store', () => {
}) })
}) })
describe('coinanimation', () => {
it('sets the state of coinanimation', () => {
const state = { coinanimation: true }
coinanimation(state, false)
expect(state.coinanimation).toEqual(false)
})
})
describe('newsletterState', () => { describe('newsletterState', () => {
it('sets the state of newsletterState', () => { it('sets the state of newsletterState', () => {
const state = { newsletterState: null } const state = { newsletterState: null }
@ -77,6 +88,32 @@ describe('Vuex store', () => {
expect(state.newsletterState).toEqual(true) expect(state.newsletterState).toEqual(true)
}) })
}) })
describe('publisherId', () => {
it('sets the state of publisherId', () => {
const state = {}
publisherId(state, 42)
expect(state.publisherId).toEqual(42)
})
})
describe('community', () => {
it('sets the state of community', () => {
const state = {}
community(state, {
name: 'test12',
description: 'test community 12',
url: 'http://test12.test12/',
registerUrl: 'http://test12.test12/vue/register',
})
expect(state.community).toEqual({
name: 'test12',
description: 'test community 12',
url: 'http://test12.test12/',
registerUrl: 'http://test12.test12/vue/register',
})
})
})
}) })
describe('actions', () => { describe('actions', () => {
@ -90,14 +127,15 @@ describe('Vuex store', () => {
firstName: 'Peter', firstName: 'Peter',
lastName: 'Lustig', lastName: 'Lustig',
description: 'Nickelbrille', description: 'Nickelbrille',
coinanimation: false,
klickTipp: { klickTipp: {
newsletterState: true, newsletterState: true,
}, },
} }
it('calls seven commits', () => { it('calls eight commits', () => {
login({ commit, state }, commitedData) login({ commit, state }, commitedData)
expect(commit).toHaveBeenCalledTimes(7) expect(commit).toHaveBeenCalledTimes(8)
}) })
it('commits email', () => { it('commits email', () => {
@ -130,9 +168,14 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille') expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille')
}) })
it('commits coinanimation', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(7, 'coinanimation', false)
})
it('commits newsletterState', () => { it('commits newsletterState', () => {
login({ commit, state }, commitedData) login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', true) expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', true)
}) })
}) })
@ -140,9 +183,9 @@ describe('Vuex store', () => {
const commit = jest.fn() const commit = jest.fn()
const state = {} const state = {}
it('calls six commits', () => { it('calls eight commits', () => {
logout({ commit, state }) logout({ commit, state })
expect(commit).toHaveBeenCalledTimes(7) expect(commit).toHaveBeenCalledTimes(8)
}) })
it('commits token', () => { it('commits token', () => {
@ -175,9 +218,14 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(6, 'description', '') expect(commit).toHaveBeenNthCalledWith(6, 'description', '')
}) })
it('commits coinanimation', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(7, 'coinanimation', true)
})
it('commits newsletterState', () => { it('commits newsletterState', () => {
logout({ commit, state }) logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(7, 'newsletterState', null) expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', null)
}) })
// how to get this working? // how to get this working?

View File

@ -67,12 +67,13 @@ describe('GddSend', () => {
it('trims the email after blur', async () => { it('trims the email after blur', async () => {
await wrapper.find('#input-group-1').find('input').setValue(' valid@email.com ') await wrapper.find('#input-group-1').find('input').setValue(' valid@email.com ')
await wrapper.find('#input-group-1').find('input').trigger('blur')
await flushPromises() await flushPromises()
expect(wrapper.vm.form.email).toBe('valid@email.com') expect(wrapper.vm.form.email).toBe('valid@email.com')
}) })
}) })
describe('ammount field', () => { describe('amount field', () => {
it('has an input field of type text', () => { it('has an input field of type text', () => {
expect(wrapper.find('#input-group-2').find('input').attributes('type')).toBe('text') expect(wrapper.find('#input-group-2').find('input').attributes('type')).toBe('text')
}) })
@ -91,6 +92,13 @@ describe('GddSend', () => {
) )
}) })
it('does not update form amount when invalid', async () => {
await wrapper.find('#input-group-2').find('input').setValue('invalid')
await wrapper.find('#input-group-2').find('input').trigger('blur')
await flushPromises()
expect(wrapper.vm.form.amountValue).toBe(0)
})
it('flushes an error message when no valid amount is given', async () => { it('flushes an error message when no valid amount is given', async () => {
await wrapper.find('#input-group-2').find('input').setValue('a') await wrapper.find('#input-group-2').find('input').setValue('a')
await flushPromises() await flushPromises()
@ -150,11 +158,11 @@ describe('GddSend', () => {
it('clears all fields on click', async () => { it('clears all fields on click', async () => {
await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv') await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv')
await wrapper.find('#input-group-2').find('input').setValue('87.23') await wrapper.find('#input-group-2').find('input').setValue('87.23')
await wrapper.find('#input-group-3').find('textarea').setValue('Long enugh') await wrapper.find('#input-group-3').find('textarea').setValue('Long enough')
await flushPromises() await flushPromises()
expect(wrapper.vm.form.email).toBe('someone@watches.tv') expect(wrapper.vm.form.email).toBe('someone@watches.tv')
expect(wrapper.vm.form.amount).toBe('87.23') expect(wrapper.vm.form.amount).toBe('87.23')
expect(wrapper.vm.form.memo).toBe('Long enugh') expect(wrapper.vm.form.memo).toBe('Long enough')
await wrapper.find('button[type="reset"]').trigger('click') await wrapper.find('button[type="reset"]').trigger('click')
await flushPromises() await flushPromises()
expect(wrapper.vm.form.email).toBe('') expect(wrapper.vm.form.email).toBe('')
@ -167,7 +175,7 @@ describe('GddSend', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv') await wrapper.find('#input-group-1').find('input').setValue('someone@watches.tv')
await wrapper.find('#input-group-2').find('input').setValue('87.23') await wrapper.find('#input-group-2').find('input').setValue('87.23')
await wrapper.find('#input-group-3').find('textarea').setValue('Long enugh') await wrapper.find('#input-group-3').find('textarea').setValue('Long enough')
await wrapper.find('form').trigger('submit') await wrapper.find('form').trigger('submit')
await flushPromises() await flushPromises()
}) })
@ -179,7 +187,7 @@ describe('GddSend', () => {
{ {
email: 'someone@watches.tv', email: 'someone@watches.tv',
amount: 87.23, amount: 87.23,
memo: 'Long enugh', memo: 'Long enough',
}, },
], ],
]) ])

View File

@ -124,12 +124,13 @@
<div class="text-center"> <div class="text-center">
<div class="text-center"> <div class="text-center">
<b-button class="test-button-back" variant="outline-secondary" to="/login"> <router-link class="test-button-back" to="/login">
{{ $t('back') }} <b-button variant="outline-secondary">
</b-button> {{ $t('back') }}
</b-button>
</router-link>
<b-button <b-button
:disabled="!(namesFilled && emailFilled && form.agree && languageFilled)" :disabled="!(namesFilled && emailFilled && form.agree && !!language)"
type="submit" type="submit"
variant="primary" variant="primary"
> >
@ -145,13 +146,11 @@
</b-row> </b-row>
</b-container> </b-container>
<div class="text-center pt-4"> <div class="text-center pt-4">
<b-button <router-link class="test-button-another-community" to="/select-community">
class="test-button-another-community" <b-button variant="outline-secondary">
variant="outline-secondary" {{ $t('community.choose-another-community') }}
to="/select-community" </b-button>
> </router-link>
{{ $t('community.choose-another-community') }}
</b-button>
</div> </div>
</div> </div>
</template> </template>
@ -239,9 +238,6 @@ export default {
emailFilled() { emailFilled() {
return this.form.email !== '' return this.form.email !== ''
}, },
languageFilled() {
return !!this.language
},
}, },
} }
</script> </script>

View File

@ -1,4 +1,4 @@
import { mount } from '@vue/test-utils' import { mount, RouterLinkStub } from '@vue/test-utils'
import RegisterCommunity from './RegisterCommunity' import RegisterCommunity from './RegisterCommunity'
const localVue = global.localVue const localVue = global.localVue
@ -23,8 +23,12 @@ describe('RegisterCommunity', () => {
}, },
} }
const stubs = {
RouterLink: RouterLinkStub,
}
const Wrapper = () => { const Wrapper = () => {
return mount(RegisterCommunity, { localVue, mocks }) return mount(RegisterCommunity, { localVue, mocks, stubs })
} }
describe('mount', () => { describe('mount', () => {

View File

@ -17,24 +17,30 @@
</b-row> </b-row>
<b-row> <b-row>
<b-col class="text-center"> <b-col class="text-center">
<b-button variant="outline-secondary" to="/register"> <router-link to="/register">
{{ $t('community.continue-to-registration') }} <b-button variant="outline-secondary">
</b-button> {{ $t('community.continue-to-registration') }}
</b-button>
</router-link>
</b-col> </b-col>
</b-row> </b-row>
<hr /> <hr />
<b-row> <b-row>
<b-col class="text-center"> <b-col class="text-center">
<b-button variant="outline-secondary" to="/select-community"> <router-link to="/select-community">
{{ $t('community.choose-another-community') }} <b-button variant="outline-secondary">
</b-button> {{ $t('community.choose-another-community') }}
</b-button>
</router-link>
</b-col> </b-col>
</b-row> </b-row>
<hr /> <hr />
<b-row> <b-row>
<b-col class="text-center"> <b-col class="text-center">
<b-button variant="outline-secondary" to="/login">{{ $t('back') }}</b-button> <router-link to="/login">
<b-button variant="outline-secondary">{{ $t('back') }}</b-button>
</router-link>
</b-col> </b-col>
</b-row> </b-row>
</div> </div>
@ -44,7 +50,7 @@
</template> </template>
<script> <script>
export default { export default {
name: 'registerSelectCommunity', name: 'registerCommunity',
data() { data() {
return {} return {}
}, },

View File

@ -1,26 +1,44 @@
import { mount } from '@vue/test-utils' import { mount, RouterLinkStub } from '@vue/test-utils'
import RegisterSelectCommunity from './RegisterSelectCommunity' import RegisterSelectCommunity from './RegisterSelectCommunity'
const localVue = global.localVue const localVue = global.localVue
const spinnerHideMock = jest.fn() const spinnerHideMock = jest.fn()
const spinnerMock = jest.fn(() => { const spinnerMock = jest.fn(() => {
return { return {
hide: spinnerHideMock, hide: spinnerHideMock,
} }
}) })
const apolloQueryMock = jest.fn().mockResolvedValue({ const apolloQueryMock = jest.fn().mockResolvedValue({
data: { data: {
communities: [ communities: [
{ {
name: 'test1', id: 1,
description: 'description 1', name: 'Gradido Entwicklung',
url: 'http://test.test/vue', description: 'Die lokale Entwicklungsumgebung von Gradido.',
url: 'http://localhost/vue/',
registerUrl: 'http://localhost/vue/register-community', registerUrl: 'http://localhost/vue/register-community',
}, },
{
id: 2,
name: 'Gradido Staging',
description: 'Der Testserver der Gradido-Akademie.',
url: 'https://stage1.gradido.net/vue/',
registerUrl: 'https://stage1.gradido.net/vue/register-community',
},
{
id: 3,
name: 'Gradido-Akademie',
description: 'Freies Institut für Wirtschaftsbionik.',
url: 'https://gradido.net',
registerUrl: 'https://gdd1.gradido.com/vue/register-community',
},
], ],
}, },
}) })
const toasterMock = jest.fn() const toasterMock = jest.fn()
describe('RegisterSelectCommunity', () => { describe('RegisterSelectCommunity', () => {
@ -52,8 +70,12 @@ describe('RegisterSelectCommunity', () => {
}, },
} }
const stubs = {
RouterLink: RouterLinkStub,
}
const Wrapper = () => { const Wrapper = () => {
return mount(RegisterSelectCommunity, { localVue, mocks }) return mount(RegisterSelectCommunity, { localVue, mocks, stubs })
} }
describe('mount', () => { describe('mount', () => {
@ -65,16 +87,40 @@ describe('RegisterSelectCommunity', () => {
expect(wrapper.find('div#register-select-community').exists()).toBeTruthy() expect(wrapper.find('div#register-select-community').exists()).toBeTruthy()
}) })
it('starts with a spinner', () => {
expect(spinnerMock).toBeCalled()
})
describe('calls the apollo query', () => { describe('calls the apollo query', () => {
beforeEach(() => { describe('server returns data', () => {
apolloQueryMock.mockRejectedValue({ it('calls the API to get the data', () => {
message: 'Wrong thing', expect(apolloQueryMock).toBeCalled()
})
it('shows two other communities', () => {
expect(wrapper.findAll('div.bg-secondary')).toHaveLength(2)
})
it('hides the spinner', () => {
expect(spinnerHideMock).toBeCalled()
}) })
wrapper = Wrapper()
}) })
it('toast an error', () => { describe('server response is error', () => {
expect(toasterMock).toBeCalledWith('Wrong thing') beforeEach(() => {
apolloQueryMock.mockRejectedValue({
message: 'Wrong thing',
})
wrapper = Wrapper()
})
it('toast an error', () => {
expect(toasterMock).toBeCalledWith('Wrong thing')
})
it('hides the spinner', () => {
expect(spinnerHideMock).toBeCalled()
})
}) })
}) })
}) })

View File

@ -4,28 +4,24 @@
<div class="pb-3">{{ $t('community.current-community') }}</div> <div class="pb-3">{{ $t('community.current-community') }}</div>
<div v-if="!pending"> <div v-if="!pending">
<div v-for="community in communities" :key="community.name"> <b-card class="border-0 mb-0" bg-variant="primary">
<b-card <b>{{ $store.state.community.name }}</b>
v-if="community.name === $store.state.community.name" <br />
class="border-0 mb-0" {{ $store.state.community.description }}
bg-variant="primary" <br />
> <router-link to="/register">
<b>{{ community.name }}</b> <b-button variant="outline-secondary">
<br />
{{ $store.state.community.description }}
<br />
<b-button variant="outline-secondary" to="/register">
{{ $t('community.continue-to-registration') }} {{ $t('community.continue-to-registration') }}
</b-button> </b-button>
</b-card> </router-link>
</div> </b-card>
<hr /> <hr />
<div>{{ $t('community.other-communities') }}</div> <div>{{ $t('community.other-communities') }}</div>
<div v-for="community in communities" :key="community.id" class="pb-3"> <div v-for="community in communities" :key="community.id" class="pb-3">
<b-card v-if="community.name != $store.state.community.name" bg-variant="secondary"> <b-card bg-variant="secondary">
<b>{{ community.name }}</b> <b>{{ community.name }}</b>
<br /> <br />
{{ community.description }} {{ community.description }}
@ -44,7 +40,9 @@
</div> </div>
<div class="text-center py-lg-4"> <div class="text-center py-lg-4">
<b-button variant="outline-secondary" to="/login">{{ $t('back') }}</b-button> <router-link to="/login">
<b-button variant="outline-secondary">{{ $t('back') }}</b-button>
</router-link>
</div> </div>
</b-container> </b-container>
</div> </div>
@ -71,7 +69,9 @@ export default {
fetchPolicy: 'network-only', fetchPolicy: 'network-only',
}) })
.then((response) => { .then((response) => {
this.communities = response.data.communities this.communities = response.data.communities.filter(
(c) => c.name !== this.$store.state.community.name,
)
}) })
.catch((error) => { .catch((error) => {
this.$toasted.error(error.message) this.$toasted.error(error.message)

View File

@ -1,5 +1,6 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import UserCardCoinAnimation from './UserCard_CoinAnimation' import UserCardCoinAnimation from './UserCard_CoinAnimation'
import { updateUserInfos } from '../../../graphql/mutations'
const localVue = global.localVue const localVue = global.localVue
@ -17,6 +18,7 @@ describe('UserCard_CoinAnimation', () => {
$store: { $store: {
state: { state: {
language: 'de', language: 'de',
coinanimation: true,
}, },
commit: storeCommitMock, commit: storeCommitMock,
}, },
@ -25,7 +27,7 @@ describe('UserCard_CoinAnimation', () => {
error: toastErrorMock, error: toastErrorMock,
}, },
$apollo: { $apollo: {
query: mockAPIcall, mutate: mockAPIcall,
}, },
} }
@ -35,6 +37,7 @@ describe('UserCard_CoinAnimation', () => {
describe('mount', () => { describe('mount', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper() wrapper = Wrapper()
}) })
@ -45,5 +48,84 @@ describe('UserCard_CoinAnimation', () => {
it('has an edit BFormCheckbox switch', () => { it('has an edit BFormCheckbox switch', () => {
expect(wrapper.find('.Test-BFormCheckbox').exists()).toBeTruthy() expect(wrapper.find('.Test-BFormCheckbox').exists()).toBeTruthy()
}) })
describe('enable with success', () => {
beforeEach(async () => {
await wrapper.setData({ CoinAnimationStatus: false })
mockAPIcall.mockResolvedValue({
data: {
updateUserInfos: {
validValues: 1,
},
},
})
await wrapper.find('input').setChecked()
})
it('calls the updateUserInfos mutation', () => {
expect(mockAPIcall).toBeCalledWith({
mutation: updateUserInfos,
variables: {
coinanimation: true,
},
})
})
it('updates the store', () => {
expect(storeCommitMock).toBeCalledWith('coinanimation', true)
})
it('toasts a success message', () => {
expect(toastSuccessMock).toBeCalledWith('settings.coinanimation.True')
})
})
describe('disable with success', () => {
beforeEach(async () => {
await wrapper.setData({ CoinAnimationStatus: true })
mockAPIcall.mockResolvedValue({
data: {
updateUserInfos: {
validValues: 1,
},
},
})
await wrapper.find('input').setChecked(false)
})
it('calls the subscribe mutation', () => {
expect(mockAPIcall).toBeCalledWith({
mutation: updateUserInfos,
variables: {
coinanimation: false,
},
})
})
it('updates the store', () => {
expect(storeCommitMock).toBeCalledWith('coinanimation', false)
})
it('toasts a success message', () => {
expect(toastSuccessMock).toBeCalledWith('settings.coinanimation.False')
})
})
describe('disable with server error', () => {
beforeEach(() => {
mockAPIcall.mockRejectedValue({
message: 'Ouch',
})
wrapper.find('input').trigger('change')
})
it('resets the CoinAnimationStatus', () => {
expect(wrapper.vm.CoinAnimationStatus).toBeTruthy()
})
it('toasts an error message', () => {
expect(toastErrorMock).toBeCalledWith('Ouch')
})
})
}) })
}) })

View File

@ -36,12 +36,9 @@ export default {
name: 'FormUserCoinAnimation', name: 'FormUserCoinAnimation',
data() { data() {
return { return {
CoinAnimationStatus: true, CoinAnimationStatus: this.$store.state.coinanimation,
} }
}, },
created() {
this.CoinAnimationStatus = this.$store.state.coinanimation /* existiert noch nicht im store */
},
methods: { methods: {
async onSubmit() { async onSubmit() {
this.$apollo this.$apollo
@ -52,7 +49,7 @@ export default {
}, },
}) })
.then(() => { .then(() => {
this.$store.state.coinanimation = this.CoinAnimationStatus this.$store.commit('coinanimation', this.CoinAnimationStatus)
this.$toasted.success( this.$toasted.success(
this.CoinAnimationStatus this.CoinAnimationStatus
? this.$t('settings.coinanimation.True') ? this.$t('settings.coinanimation.True')
@ -60,6 +57,7 @@ export default {
) )
}) })
.catch((error) => { .catch((error) => {
this.CoinAnimationStatus = this.$store.state.coinanimation
this.$toasted.error(error.message) this.$toasted.error(error.message)
}) })
}, },

View File

@ -17,7 +17,6 @@ describe('UserCard_FormUserData', () => {
$t: jest.fn((t) => t), $t: jest.fn((t) => t),
$store: { $store: {
state: { state: {
email: 'user@example.org',
firstName: 'Peter', firstName: 'Peter',
lastName: 'Lustig', lastName: 'Lustig',
description: '', description: '',
@ -117,7 +116,6 @@ describe('UserCard_FormUserData', () => {
expect(mockAPIcall).toBeCalledWith( expect(mockAPIcall).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
firstName: 'Petra', firstName: 'Petra',
lastName: 'Lustiger', lastName: 'Lustiger',
description: 'Keine Nickelbrille', description: 'Keine Nickelbrille',
@ -165,7 +163,6 @@ describe('UserCard_FormUserData', () => {
expect(mockAPIcall).toBeCalledWith( expect(mockAPIcall).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
firstName: 'Petra', firstName: 'Petra',
lastName: 'Lustiger', lastName: 'Lustiger',
description: 'Keine Nickelbrille', description: 'Keine Nickelbrille',

View File

@ -111,7 +111,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
firstName: this.form.firstName, firstName: this.form.firstName,
lastName: this.form.lastName, lastName: this.form.lastName,
description: this.form.description, description: this.form.description,

View File

@ -75,7 +75,6 @@ describe('UserCard_FormUserMail', () => {
expect(mockAPIcall).toHaveBeenCalledWith( expect(mockAPIcall).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
newEmail: 'test@example.org', newEmail: 'test@example.org',
}, },
}), }),
@ -104,7 +103,6 @@ describe('UserCard_FormUserMail', () => {
expect(mockAPIcall).toHaveBeenCalledWith( expect(mockAPIcall).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
newEmail: 'test@example.org', newEmail: 'test@example.org',
}, },
}), }),

View File

@ -48,7 +48,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
newEmail: this.newEmail, newEmail: this.newEmail,
}, },
}) })

View File

@ -15,11 +15,6 @@ describe('UserCard_FormUserPasswort', () => {
const mocks = { const mocks = {
$t: jest.fn((t) => t), $t: jest.fn((t) => t),
$store: {
state: {
email: 'user@example.org',
},
},
$toasted: { $toasted: {
success: toastSuccessMock, success: toastSuccessMock,
error: toastErrorMock, error: toastErrorMock,
@ -191,7 +186,6 @@ describe('UserCard_FormUserPasswort', () => {
expect(changePasswordProfileMock).toHaveBeenCalledWith( expect(changePasswordProfileMock).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
password: '1234', password: '1234',
passwordNew: 'Aa123456_', passwordNew: 'Aa123456_',
}, },

View File

@ -76,7 +76,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
password: this.form.password, password: this.form.password,
passwordNew: this.form.newPassword.password, passwordNew: this.form.newPassword.password,
}, },

View File

@ -25,7 +25,6 @@ describe('UserCard_FormUsername', () => {
$t: jest.fn((t) => t), $t: jest.fn((t) => t),
$store: { $store: {
state: { state: {
email: 'user@example.org',
username: '', username: '',
}, },
commit: storeCommitMock, commit: storeCommitMock,
@ -109,7 +108,6 @@ describe('UserCard_FormUsername', () => {
expect(mockAPIcall).toHaveBeenCalledWith( expect(mockAPIcall).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
username: 'username', username: 'username',
}, },
}), }),
@ -148,7 +146,6 @@ describe('UserCard_FormUsername', () => {
expect(mockAPIcall).toHaveBeenCalledWith( expect(mockAPIcall).toHaveBeenCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'user@example.org',
username: 'username', username: 'username',
}, },
}), }),

View File

@ -90,7 +90,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
username: this.form.username, username: this.form.username,
}, },
}) })

View File

@ -23,7 +23,6 @@ describe('UserCard_Language', () => {
$store: { $store: {
state: { state: {
language: 'de', language: 'de',
email: 'peter@lustig.de',
}, },
commit: storeCommitMock, commit: storeCommitMock,
}, },
@ -127,7 +126,6 @@ describe('UserCard_Language', () => {
expect(mockAPIcall).toBeCalledWith( expect(mockAPIcall).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
variables: { variables: {
email: 'peter@lustig.de',
locale: 'en', locale: 'en',
}, },
}), }),

View File

@ -89,7 +89,6 @@ export default {
.mutate({ .mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
variables: { variables: {
email: this.$store.state.email,
locale: this.language, locale: this.language,
}, },
}) })

View File

@ -38,6 +38,7 @@ describe('UserCard_Newsletter', () => {
describe('mount', () => { describe('mount', () => {
beforeEach(() => { beforeEach(() => {
jest.clearAllMocks()
wrapper = Wrapper() wrapper = Wrapper()
}) })
@ -51,13 +52,13 @@ describe('UserCard_Newsletter', () => {
describe('unsubscribe with success', () => { describe('unsubscribe with success', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.setData({ newsletterState: false }) await wrapper.setData({ newsletterState: true })
mockAPIcall.mockResolvedValue({ mockAPIcall.mockResolvedValue({
data: { data: {
unsubscribeNewsletter: true, unsubscribeNewsletter: true,
}, },
}) })
await wrapper.find('input').trigger('change') await wrapper.find('input').setChecked(false)
}) })
it('calls the unsubscribe mutation', () => { it('calls the unsubscribe mutation', () => {
@ -80,13 +81,13 @@ describe('UserCard_Newsletter', () => {
describe('subscribe with success', () => { describe('subscribe with success', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.setData({ newsletterState: true }) await wrapper.setData({ newsletterState: false })
mockAPIcall.mockResolvedValue({ mockAPIcall.mockResolvedValue({
data: { data: {
subscribeNewsletter: true, subscribeNewsletter: true,
}, },
}) })
wrapper.find('input').trigger('change') await wrapper.find('input').setChecked()
}) })
it('calls the subscribe mutation', () => { it('calls the subscribe mutation', () => {
@ -104,7 +105,7 @@ describe('UserCard_Newsletter', () => {
}) })
it('toasts a success message', () => { it('toasts a success message', () => {
expect(toastSuccessMock).toBeCalledWith('settings.newsletter.newsletterFalse') expect(toastSuccessMock).toBeCalledWith('settings.newsletter.newsletterTrue')
}) })
}) })

View File

@ -13,7 +13,7 @@ import VueQrcode from 'vue-qrcode'
import VueMoment from 'vue-moment' import VueMoment from 'vue-moment'
import clickOutside from '@/directives/click-ouside.js' // import clickOutside from '@/directives/click-ouside.js'
import { focus } from 'vue-focus' import { focus } from 'vue-focus'
import { loadAllRules } from '../src/validation-rules' import { loadAllRules } from '../src/validation-rules'
@ -46,7 +46,7 @@ global.localVue.use(VueQrcode)
global.localVue.use(VueMoment) global.localVue.use(VueMoment)
global.localVue.component('validation-provider', ValidationProvider) global.localVue.component('validation-provider', ValidationProvider)
global.localVue.component('validation-observer', ValidationObserver) global.localVue.component('validation-observer', ValidationObserver)
global.localVue.directive('click-outside', clickOutside) // global.localVue.directive('click-outside', clickOutside)
global.localVue.directive('focus', focus) global.localVue.directive('focus', focus)
// throw errors for vue warnings to force the programmers to take care about warnings // throw errors for vue warnings to force the programmers to take care about warnings

View File

@ -150,16 +150,16 @@ Poco::JSON::Object* JsonCreateUser::handle(Poco::Dynamic::Var params)
emailOptIn->setBaseUrl(user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath); emailOptIn->setBaseUrl(user->getGroupBaseUrl() + ServerConfig::g_frontend_checkEmailPath);
em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType))); em->addEmail(new model::Email(emailOptIn, user, model::Email::convertTypeFromInt(emailType)));
Poco::JSON::Object* result = stateSuccess();
result->set("user", user->getJson());
if (login_after_register && session) { if (login_after_register && session) {
Poco::JSON::Object* result = stateSuccess();
if(group_was_not_set) { if(group_was_not_set) {
Poco::JSON::Array infos; Poco::JSON::Array infos;
infos.add("group_id was not set, use 1 as default!"); infos.add("group_id was not set, use 1 as default!");
result->set("info", infos); result->set("info", infos);
} }
result->set("session_id", session->getHandle()); result->set("session_id", session->getHandle());
return result;
} }
return stateSuccess(); return result;
} }