mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 1574-Concept_to_introduce_Gradido-ID
This commit is contained in:
commit
54542ef9ad
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -528,7 +528,7 @@ jobs:
|
||||
report_name: Coverage Backend
|
||||
type: lcov
|
||||
result_path: ./backend/coverage/lcov.info
|
||||
min_coverage: 68
|
||||
min_coverage: 74
|
||||
token: ${{ github.token }}
|
||||
|
||||
##########################################################################
|
||||
|
||||
@ -5,6 +5,7 @@ const localVue = global.localVue
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => d),
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import ContributionLinkForm from './ContributionLinkForm.vue'
|
||||
import { toastErrorSpy } from '../../test/testSetup'
|
||||
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
|
||||
import { createContributionLink } from '@/graphql/createContributionLink.js'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
@ -72,48 +73,70 @@ describe('ContributionLinkForm', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// describe('successfull submit', () => {
|
||||
// beforeEach(async () => {
|
||||
// mockAPIcall.mockResolvedValue({
|
||||
// data: {
|
||||
// createContributionLink: {
|
||||
// link: 'https://localhost/redeem/CL-1a2345678',
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
// await wrapper.find('input.test-validFrom').setValue('2022-6-18')
|
||||
// await wrapper.find('input.test-validTo').setValue('2022-7-18')
|
||||
// await wrapper.find('input.test-name').setValue('test name')
|
||||
// await wrapper.find('input.test-memo').setValue('test memo')
|
||||
// await wrapper.find('input.test-amount').setValue('100')
|
||||
// await wrapper.find('form').trigger('submit')
|
||||
// })
|
||||
describe('successfull submit', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockResolvedValue({
|
||||
data: {
|
||||
createContributionLink: {
|
||||
link: 'https://localhost/redeem/CL-1a2345678',
|
||||
},
|
||||
},
|
||||
})
|
||||
await wrapper
|
||||
.findAllComponents({ name: 'BFormDatepicker' })
|
||||
.at(0)
|
||||
.vm.$emit('input', '2022-6-18')
|
||||
await wrapper
|
||||
.findAllComponents({ name: 'BFormDatepicker' })
|
||||
.at(1)
|
||||
.vm.$emit('input', '2022-7-18')
|
||||
await wrapper.find('input.test-name').setValue('test name')
|
||||
await wrapper.find('textarea.test-memo').setValue('test memo')
|
||||
await wrapper.find('input.test-amount').setValue('100')
|
||||
await wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
// it('calls the API', () => {
|
||||
// expect(mockAPIcall).toHaveBeenCalledWith(
|
||||
// expect.objectContaining({
|
||||
// variables: {
|
||||
// link: 'https://localhost/redeem/CL-1a2345678',
|
||||
// },
|
||||
// }),
|
||||
// )
|
||||
// })
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toHaveBeenCalledWith({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
validFrom: '2022-6-18',
|
||||
validTo: '2022-7-18',
|
||||
name: 'test name',
|
||||
amount: '100',
|
||||
memo: 'test memo',
|
||||
cycle: 'ONCE',
|
||||
maxPerCycle: 1,
|
||||
maxAmountPerMonth: '0',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
// it('displays the new username', () => {
|
||||
// expect(wrapper.find('div.display-username').text()).toEqual('@username')
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
describe('send createContributionLink with error', () => {
|
||||
beforeEach(() => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })
|
||||
wrapper = Wrapper()
|
||||
wrapper.vm.onSubmit()
|
||||
it('toasts a succes message', () => {
|
||||
expect(toastSuccessSpy).toBeCalledWith('https://localhost/redeem/CL-1a2345678')
|
||||
})
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('contributionLink.noStartDate')
|
||||
describe('send createContributionLink with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'OUCH!' })
|
||||
await wrapper
|
||||
.findAllComponents({ name: 'BFormDatepicker' })
|
||||
.at(0)
|
||||
.vm.$emit('input', '2022-6-18')
|
||||
await wrapper
|
||||
.findAllComponents({ name: 'BFormDatepicker' })
|
||||
.at(1)
|
||||
.vm.$emit('input', '2022-7-18')
|
||||
await wrapper.find('input.test-name').setValue('test name')
|
||||
await wrapper.find('textarea.test-memo').setValue('test memo')
|
||||
await wrapper.find('input.test-amount').setValue('100')
|
||||
await wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<div class="contribution-link-form">
|
||||
<div v-if="updateData" class="text-light bg-info p-3">
|
||||
{{ updateData }}
|
||||
</div>
|
||||
<b-form class="m-5" @submit.prevent="onSubmit" ref="contributionLinkForm">
|
||||
<!-- Date -->
|
||||
<b-row>
|
||||
@ -68,34 +65,32 @@
|
||||
class="test-amount"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
<b-collapse id="collapse-2">
|
||||
<b-jumbotron>
|
||||
<b-row class="mb-4">
|
||||
<b-col>
|
||||
<!-- Cycle -->
|
||||
<label for="cycle">{{ $t('contributionLink.cycle') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.cycle"
|
||||
:options="cycle"
|
||||
:disabled="disabled"
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<!-- maxPerCycle -->
|
||||
<label for="maxPerCycle">{{ $t('contributionLink.maxPerCycle') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.maxPerCycle"
|
||||
:options="maxPerCycle"
|
||||
:disabled="disabled"
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="mb-4">
|
||||
<b-col>
|
||||
<!-- Cycle -->
|
||||
<label for="cycle">{{ $t('contributionLink.cycle') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.cycle"
|
||||
:options="cycle"
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
<b-col>
|
||||
<!-- maxPerCycle -->
|
||||
<label for="maxPerCycle">{{ $t('contributionLink.maxPerCycle') }}</label>
|
||||
<b-form-select
|
||||
v-model="form.maxPerCycle"
|
||||
:options="maxPerCycle"
|
||||
:disabled="disabled"
|
||||
class="mb-3"
|
||||
size="lg"
|
||||
></b-form-select>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- Max amount -->
|
||||
<!-- Max amount -->
|
||||
<!--
|
||||
<b-form-group :label="$t('contributionLink.maximumAmount')">
|
||||
<b-form-input
|
||||
v-model="form.maxAmountPerMonth"
|
||||
@ -105,8 +100,7 @@
|
||||
placeholder="0"
|
||||
></b-form-input>
|
||||
</b-form-group>
|
||||
</b-jumbotron>
|
||||
</b-collapse>
|
||||
-->
|
||||
<div class="mt-6">
|
||||
<b-button type="submit" variant="primary">{{ $t('contributionLink.create') }}</b-button>
|
||||
<b-button type="reset" variant="danger" @click.prevent="onReset">
|
||||
@ -143,18 +137,18 @@ export default {
|
||||
min: new Date(),
|
||||
cycle: [
|
||||
{ value: 'ONCE', text: this.$t('contributionLink.options.cycle.once') },
|
||||
{ value: 'hourly', text: this.$t('contributionLink.options.cycle.hourly') },
|
||||
{ value: 'daily', text: this.$t('contributionLink.options.cycle.daily') },
|
||||
{ value: 'weekly', text: this.$t('contributionLink.options.cycle.weekly') },
|
||||
{ value: 'monthly', text: this.$t('contributionLink.options.cycle.monthly') },
|
||||
{ value: 'yearly', text: this.$t('contributionLink.options.cycle.yearly') },
|
||||
// { value: 'hourly', text: this.$t('contributionLink.options.cycle.hourly') },
|
||||
{ value: 'DAILY', text: this.$t('contributionLink.options.cycle.daily') },
|
||||
// { value: 'weekly', text: this.$t('contributionLink.options.cycle.weekly') },
|
||||
// { value: 'monthly', text: this.$t('contributionLink.options.cycle.monthly') },
|
||||
// { value: 'yearly', text: this.$t('contributionLink.options.cycle.yearly') },
|
||||
],
|
||||
maxPerCycle: [
|
||||
{ value: '1', text: '1 x' },
|
||||
{ value: '2', text: '2 x' },
|
||||
{ value: '3', text: '3 x' },
|
||||
{ value: '4', text: '4 x' },
|
||||
{ value: '5', text: '5 x' },
|
||||
// { value: '2', text: '2 x' },
|
||||
// { value: '3', text: '3 x' },
|
||||
// { value: '4', text: '4 x' },
|
||||
// { value: '5', text: '5 x' },
|
||||
],
|
||||
}
|
||||
},
|
||||
@ -195,12 +189,8 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
updateData() {
|
||||
return this.contributionLinkData
|
||||
},
|
||||
disabled() {
|
||||
if (this.form.cycle === 'ONCE') return true
|
||||
return false
|
||||
return true
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
@ -9,6 +9,7 @@ const mockAPIcall = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$d: jest.fn((d) => d),
|
||||
$apollo: {
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
|
||||
@ -64,8 +64,28 @@ export default {
|
||||
'amount',
|
||||
{ key: 'cycle', label: this.$t('contributionLink.cycle') },
|
||||
{ key: 'maxPerCycle', label: this.$t('contributionLink.maxPerCycle') },
|
||||
{ key: 'validFrom', label: this.$t('contributionLink.validFrom') },
|
||||
{ key: 'validTo', label: this.$t('contributionLink.validTo') },
|
||||
{
|
||||
key: 'validFrom',
|
||||
label: this.$t('contributionLink.validFrom'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'validTo',
|
||||
label: this.$t('contributionLink.validTo'),
|
||||
formatter: (value, key, item) => {
|
||||
if (value) {
|
||||
return this.$d(new Date(value))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
},
|
||||
'delete',
|
||||
'edit',
|
||||
'show',
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
value-field="item"
|
||||
text-field="name"
|
||||
name="month-selection"
|
||||
:disabled="true"
|
||||
></b-form-radio-group>
|
||||
</b-row>
|
||||
<div class="m-4">
|
||||
|
||||
@ -3,11 +3,15 @@ import NavBar from './NavBar.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloMutateMock = jest.fn()
|
||||
const storeDispatchMock = jest.fn()
|
||||
const routerPushMock = jest.fn()
|
||||
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$apollo: {
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
openCreations: 1,
|
||||
@ -69,5 +73,9 @@ describe('NavBar', () => {
|
||||
it('dispatches logout to store', () => {
|
||||
expect(storeDispatchMock).toBeCalledWith('logout')
|
||||
})
|
||||
|
||||
it('has called logout mutation', () => {
|
||||
expect(apolloMutateMock).toBeCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -28,14 +28,18 @@
|
||||
</template>
|
||||
<script>
|
||||
import CONFIG from '../config'
|
||||
import { logout } from '../graphql/logout'
|
||||
|
||||
export default {
|
||||
name: 'navbar',
|
||||
methods: {
|
||||
logout() {
|
||||
async logout() {
|
||||
window.location.assign(CONFIG.WALLET_URL)
|
||||
// window.location = CONFIG.WALLET_URL
|
||||
this.$store.dispatch('logout')
|
||||
await this.$apollo.mutate({
|
||||
mutation: logout,
|
||||
})
|
||||
},
|
||||
wallet() {
|
||||
window.location = CONFIG.WALLET_AUTH_URL.replace('{token}', this.$store.state.token)
|
||||
|
||||
7
admin/src/graphql/logout.js
Normal file
7
admin/src/graphql/logout.js
Normal file
@ -0,0 +1,7 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const logout = gql`
|
||||
mutation {
|
||||
logout
|
||||
}
|
||||
`
|
||||
@ -9,7 +9,6 @@
|
||||
"cycle": "Zyklus",
|
||||
"deleted": "Automatische Schöpfung gelöscht!",
|
||||
"deleteNow": "Automatische Creations '{name}' wirklich löschen?",
|
||||
"maximumAmount": "maximaler Betrag",
|
||||
"maxPerCycle": "Wiederholungen",
|
||||
"memo": "Nachricht",
|
||||
"name": "Name",
|
||||
@ -21,11 +20,7 @@
|
||||
"options": {
|
||||
"cycle": {
|
||||
"daily": "täglich",
|
||||
"hourly": "stündlich",
|
||||
"monthly": "monatlich",
|
||||
"once": "einmalig",
|
||||
"weekly": "wöchentlich",
|
||||
"yearly": "jährlich"
|
||||
"once": "einmalig"
|
||||
}
|
||||
},
|
||||
"validFrom": "Startdatum",
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
"cycle": "Cycle",
|
||||
"deleted": "Automatic creation deleted!",
|
||||
"deleteNow": "Do you really delete automatic creations '{name}'?",
|
||||
"maximumAmount": "Maximum amount",
|
||||
"maxPerCycle": "Repetition",
|
||||
"memo": "Memo",
|
||||
"name": "Name",
|
||||
@ -21,11 +20,7 @@
|
||||
"options": {
|
||||
"cycle": {
|
||||
"daily": "daily",
|
||||
"hourly": "hourly",
|
||||
"monthly": "monthly",
|
||||
"once": "once",
|
||||
"weekly": "weekly",
|
||||
"yearly": "yearly"
|
||||
"once": "once"
|
||||
}
|
||||
},
|
||||
"validFrom": "Start-date",
|
||||
|
||||
@ -78,6 +78,7 @@ const storeCommitMock = jest.fn()
|
||||
const mocks = {
|
||||
$t: jest.fn((t) => t),
|
||||
$n: jest.fn((n) => n),
|
||||
$d: jest.fn((d) => d),
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
|
||||
@ -5,6 +5,7 @@ module.exports = {
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
|
||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||
setupFilesAfterEnv: ['<rootDir>/test/extensions.ts'],
|
||||
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||
moduleNameMapper: {
|
||||
'@/(.*)': '<rootDir>/src/$1',
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import { registerEnumType } from 'type-graphql'
|
||||
|
||||
// lowercase values are not implemented yet
|
||||
export enum ContributionCycleType {
|
||||
ONCE = 'once',
|
||||
ONCE = 'ONCE',
|
||||
HOUR = 'hour',
|
||||
TWO_HOURS = 'two_hours',
|
||||
FOUR_HOURS = 'four_hours',
|
||||
EIGHT_HOURS = 'eight_hours',
|
||||
HALF_DAY = 'half_day',
|
||||
DAY = 'day',
|
||||
DAILY = 'DAILY',
|
||||
TWO_DAYS = 'two_days',
|
||||
THREE_DAYS = 'three_days',
|
||||
FOUR_DAYS = 'four_days',
|
||||
|
||||
@ -13,6 +13,7 @@ import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||
import {
|
||||
login,
|
||||
setUserRole,
|
||||
deleteUser,
|
||||
unDeleteUser,
|
||||
@ -27,7 +28,6 @@ import {
|
||||
} from '@/seeds/graphql/mutations'
|
||||
import {
|
||||
listUnconfirmedContributions,
|
||||
login,
|
||||
searchUsers,
|
||||
listTransactionLinksAdmin,
|
||||
listContributionLinks,
|
||||
@ -96,8 +96,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -121,8 +121,8 @@ describe('AdminResolver', () => {
|
||||
describe('with admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
admin = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -249,8 +249,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -274,8 +274,8 @@ describe('AdminResolver', () => {
|
||||
describe('with admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
admin = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -357,8 +357,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -382,8 +382,8 @@ describe('AdminResolver', () => {
|
||||
describe('with admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
admin = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -469,8 +469,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -514,8 +514,8 @@ describe('AdminResolver', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
admin = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
|
||||
@ -766,8 +766,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -875,8 +875,8 @@ describe('AdminResolver', () => {
|
||||
describe('with admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
admin = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -1202,7 +1202,8 @@ describe('AdminResolver', () => {
|
||||
})
|
||||
|
||||
describe('creation update is not valid', () => {
|
||||
it('throws an error', async () => {
|
||||
// as this test has not clearly defined that date, it is a false positive
|
||||
it.skip('throws an error', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: adminUpdateContribution,
|
||||
@ -1227,7 +1228,8 @@ describe('AdminResolver', () => {
|
||||
})
|
||||
|
||||
describe('creation update is successful changing month', () => {
|
||||
it('returns update creation object', async () => {
|
||||
// skipped as changing the month is currently disable
|
||||
it.skip('returns update creation object', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: adminUpdateContribution,
|
||||
@ -1255,7 +1257,8 @@ describe('AdminResolver', () => {
|
||||
})
|
||||
|
||||
describe('creation update is successful without changing month', () => {
|
||||
it('returns update creation object', async () => {
|
||||
// actually this mutation IS changing the month
|
||||
it.skip('returns update creation object', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: adminUpdateContribution,
|
||||
@ -1299,10 +1302,10 @@ describe('AdminResolver', () => {
|
||||
lastName: 'Lustig',
|
||||
email: 'peter@lustig.de',
|
||||
date: expect.any(String),
|
||||
memo: 'Das war leider zu Viel!',
|
||||
amount: '200',
|
||||
memo: 'Herzlich Willkommen bei Gradido!',
|
||||
amount: '400',
|
||||
moderator: admin.id,
|
||||
creation: ['1000', '1000', '300'],
|
||||
creation: ['1000', '600', '500'],
|
||||
},
|
||||
{
|
||||
id: expect.any(Number),
|
||||
@ -1313,7 +1316,7 @@ describe('AdminResolver', () => {
|
||||
memo: 'Grundeinkommen',
|
||||
amount: '500',
|
||||
moderator: admin.id,
|
||||
creation: ['1000', '1000', '300'],
|
||||
creation: ['1000', '600', '500'],
|
||||
},
|
||||
{
|
||||
id: expect.any(Number),
|
||||
@ -1556,8 +1559,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -1602,8 +1605,8 @@ describe('AdminResolver', () => {
|
||||
}
|
||||
|
||||
// admin: only now log in
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -1792,13 +1795,14 @@ describe('AdminResolver', () => {
|
||||
})
|
||||
|
||||
describe('Contribution Links', () => {
|
||||
const now = new Date()
|
||||
const variables = {
|
||||
amount: new Decimal(200),
|
||||
name: 'Dokumenta 2022',
|
||||
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||
cycle: 'once',
|
||||
validFrom: new Date(2022, 5, 18).toISOString(),
|
||||
validTo: new Date(2022, 7, 14).toISOString(),
|
||||
validTo: new Date(now.getFullYear() + 1, 7, 14).toISOString(),
|
||||
maxAmountPerMonth: new Decimal(200),
|
||||
maxPerCycle: 1,
|
||||
}
|
||||
@ -1862,8 +1866,8 @@ describe('AdminResolver', () => {
|
||||
describe('without admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -1936,8 +1940,8 @@ describe('AdminResolver', () => {
|
||||
describe('with admin rights', () => {
|
||||
beforeAll(async () => {
|
||||
user = await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -1980,7 +1984,7 @@ describe('AdminResolver', () => {
|
||||
name: 'Dokumenta 2022',
|
||||
memo: 'Danke für deine Teilnahme an der Dokumenta 2022',
|
||||
validFrom: new Date('2022-06-18T00:00:00.000Z'),
|
||||
validTo: new Date('2022-08-14T00:00:00.000Z'),
|
||||
validTo: expect.any(Date),
|
||||
cycle: 'once',
|
||||
maxPerCycle: 1,
|
||||
totalMaxCountOfContribution: null,
|
||||
@ -1990,8 +1994,8 @@ describe('AdminResolver', () => {
|
||||
deletedAt: null,
|
||||
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||
linkEnabled: true,
|
||||
// amount: '200',
|
||||
// maxAmountPerMonth: '200',
|
||||
amount: expect.decimalEqual(200),
|
||||
maxAmountPerMonth: expect.decimalEqual(200),
|
||||
}),
|
||||
)
|
||||
})
|
||||
@ -2280,7 +2284,7 @@ describe('AdminResolver', () => {
|
||||
id: linkId,
|
||||
name: 'Dokumenta 2023',
|
||||
memo: 'Danke für deine Teilnahme an der Dokumenta 2023',
|
||||
// amount: '400',
|
||||
amount: expect.decimalEqual(400),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@ -339,6 +339,9 @@ export class AdminResolver {
|
||||
let creations = await getUserCreation(user.id)
|
||||
if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) {
|
||||
creations = updateCreations(creations, contributionToUpdate)
|
||||
} else {
|
||||
logger.error('Currently the month of the contribution cannot change.')
|
||||
throw new Error('Currently the month of the contribution cannot change.')
|
||||
}
|
||||
|
||||
// all possible cases not to be true are thrown in this function
|
||||
@ -675,6 +678,7 @@ export class AdminResolver {
|
||||
{ currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated,
|
||||
): Promise<ContributionLinkList> {
|
||||
const [links, count] = await DbContributionLink.findAndCount({
|
||||
where: [{ validTo: MoreThan(new Date()) }, { validTo: IsNull() }],
|
||||
order: { createdAt: order },
|
||||
skip: (currentPage - 1) * pageSize,
|
||||
take: pageSize,
|
||||
|
||||
@ -7,8 +7,9 @@ import {
|
||||
adminCreateContributionMessage,
|
||||
createContribution,
|
||||
createContributionMessage,
|
||||
login,
|
||||
} from '@/seeds/graphql/mutations'
|
||||
import { listContributionMessages, login } from '@/seeds/graphql/queries'
|
||||
import { listContributionMessages } from '@/seeds/graphql/queries'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
@ -21,14 +22,13 @@ jest.mock('@/mailer/sendAddedContributionMessageEmail', () => {
|
||||
}
|
||||
})
|
||||
|
||||
let mutate: any, query: any, con: any
|
||||
let mutate: any, con: any
|
||||
let testEnv: any
|
||||
let result: any
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
mutate = testEnv.mutate
|
||||
query = testEnv.query
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
})
|
||||
@ -59,8 +59,8 @@ describe('ContributionMessageResolver', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
result = await mutate({
|
||||
@ -71,8 +71,8 @@ describe('ContributionMessageResolver', () => {
|
||||
creationDate: new Date().toString(),
|
||||
},
|
||||
})
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -103,8 +103,8 @@ describe('ContributionMessageResolver', () => {
|
||||
})
|
||||
|
||||
it('throws error when contribution.userId equals user.id', async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
const result2 = await mutate({
|
||||
@ -195,8 +195,8 @@ describe('ContributionMessageResolver', () => {
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeAll(async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -227,8 +227,8 @@ describe('ContributionMessageResolver', () => {
|
||||
})
|
||||
|
||||
it('throws error when other user tries to send createContributionMessage', async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
await expect(
|
||||
@ -253,8 +253,8 @@ describe('ContributionMessageResolver', () => {
|
||||
|
||||
describe('valid input', () => {
|
||||
beforeAll(async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -304,8 +304,8 @@ describe('ContributionMessageResolver', () => {
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeAll(async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
|
||||
@ -8,8 +8,9 @@ import {
|
||||
createContribution,
|
||||
deleteContribution,
|
||||
updateContribution,
|
||||
login,
|
||||
} from '@/seeds/graphql/mutations'
|
||||
import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries'
|
||||
import { listAllContributions, listContributions } from '@/seeds/graphql/queries'
|
||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
@ -54,8 +55,8 @@ describe('ContributionResolver', () => {
|
||||
describe('authenticated with valid user', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -197,8 +198,8 @@ describe('ContributionResolver', () => {
|
||||
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await creationFactory(testEnv, bibisCreation!)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
await mutate({
|
||||
@ -310,8 +311,8 @@ describe('ContributionResolver', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
result = await mutate({
|
||||
@ -393,8 +394,8 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('wrong user tries to update the contribution', () => {
|
||||
beforeAll(async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -445,8 +446,8 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('update too much so that the limit is exceeded', () => {
|
||||
beforeAll(async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
@ -489,9 +490,7 @@ describe('ContributionResolver', () => {
|
||||
}),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
new GraphQLError('No information for available creations for the given date'),
|
||||
],
|
||||
errors: [new GraphQLError('Currently the month of the contribution cannot change.')],
|
||||
}),
|
||||
)
|
||||
})
|
||||
@ -553,8 +552,8 @@ describe('ContributionResolver', () => {
|
||||
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await creationFactory(testEnv, bibisCreation!)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
await mutate({
|
||||
@ -630,8 +629,8 @@ describe('ContributionResolver', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
result = await mutate({
|
||||
@ -668,8 +667,8 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('other user sends a deleteContribtuion', () => {
|
||||
it('returns an error', async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
await expect(
|
||||
@ -702,8 +701,8 @@ describe('ContributionResolver', () => {
|
||||
|
||||
describe('User deletes already confirmed contribution', () => {
|
||||
it('throws an error', async () => {
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
await mutate({
|
||||
@ -712,8 +711,8 @@ describe('ContributionResolver', () => {
|
||||
id: result.data.createContribution.id,
|
||||
},
|
||||
})
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||
})
|
||||
await expect(
|
||||
|
||||
@ -164,6 +164,9 @@ export class ContributionResolver {
|
||||
let creations = await getUserCreation(user.id)
|
||||
if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) {
|
||||
creations = updateCreations(creations, contributionToUpdate)
|
||||
} else {
|
||||
logger.error('Currently the month of the contribution cannot change.')
|
||||
throw new Error('Currently the month of the contribution cannot change.')
|
||||
}
|
||||
|
||||
// all possible cases not to be true are thrown in this function
|
||||
|
||||
@ -1,4 +1,168 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { transactionLinkCode } from './TransactionLinkResolver'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { login, createContributionLink, redeemTransactionLink } from '@/seeds/graphql/mutations'
|
||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { GraphQLError } from 'graphql'
|
||||
|
||||
let mutate: any, con: any
|
||||
let testEnv: any
|
||||
|
||||
beforeAll(async () => {
|
||||
testEnv = await testEnvironment()
|
||||
mutate = testEnv.mutate
|
||||
con = testEnv.con
|
||||
await cleanDB()
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDB()
|
||||
await con.close()
|
||||
})
|
||||
|
||||
describe('TransactionLinkResolver', () => {
|
||||
describe('redeem daily Contribution Link', () => {
|
||||
const now = new Date()
|
||||
let contributionLink: DbContributionLink | undefined
|
||||
|
||||
beforeAll(async () => {
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
await mutate({
|
||||
mutation: createContributionLink,
|
||||
variables: {
|
||||
amount: new Decimal(5),
|
||||
name: 'Daily Contribution Link',
|
||||
memo: 'Thank you for contribute daily to the community',
|
||||
cycle: 'DAILY',
|
||||
validFrom: new Date(now.getFullYear(), 0, 1).toISOString(),
|
||||
validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999).toISOString(),
|
||||
maxAmountPerMonth: new Decimal(200),
|
||||
maxPerCycle: 1,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('has a daily contribution link in the database', async () => {
|
||||
const cls = await DbContributionLink.find()
|
||||
expect(cls).toHaveLength(1)
|
||||
contributionLink = cls[0]
|
||||
expect(contributionLink).toEqual(
|
||||
expect.objectContaining({
|
||||
id: expect.any(Number),
|
||||
name: 'Daily Contribution Link',
|
||||
memo: 'Thank you for contribute daily to the community',
|
||||
validFrom: new Date(now.getFullYear(), 0, 1),
|
||||
validTo: new Date(now.getFullYear(), 11, 31, 23, 59, 59, 0),
|
||||
cycle: 'DAILY',
|
||||
maxPerCycle: 1,
|
||||
totalMaxCountOfContribution: null,
|
||||
maxAccountBalance: null,
|
||||
minGapHours: null,
|
||||
createdAt: expect.any(Date),
|
||||
deletedAt: null,
|
||||
code: expect.stringMatching(/^[0-9a-f]{24,24}$/),
|
||||
linkEnabled: true,
|
||||
amount: expect.decimalEqual(5),
|
||||
maxAmountPerMonth: expect.decimalEqual(200),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('allows the user to redeem the contribution link', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'CL-' + (contributionLink ? contributionLink.code : ''),
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
redeemTransactionLink: true,
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'CL-' + (contributionLink ? contributionLink.code : ''),
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
new GraphQLError(
|
||||
'Creation from contribution link was not successful. Error: Contribution link already redeemed today',
|
||||
),
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
describe('after one day', () => {
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers()
|
||||
/* eslint-disable-next-line @typescript-eslint/no-empty-function */
|
||||
setTimeout(() => {}, 1000 * 60 * 60 * 24)
|
||||
jest.runAllTimers()
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers()
|
||||
})
|
||||
|
||||
it('allows the user to redeem the contribution link again', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'CL-' + (contributionLink ? contributionLink.code : ''),
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
data: {
|
||||
redeemTransactionLink: true,
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'CL-' + (contributionLink ? contributionLink.code : ''),
|
||||
},
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
errors: [
|
||||
new GraphQLError(
|
||||
'Creation from contribution link was not successful. Error: Contribution link already redeemed today',
|
||||
),
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('transactionLinkCode', () => {
|
||||
const date = new Date()
|
||||
|
||||
@ -34,6 +34,7 @@ import { getUserCreation, validateContribution } from './util/creations'
|
||||
import { Decay } from '@model/Decay'
|
||||
import Decimal from 'decimal.js-light'
|
||||
import { TransactionTypeId } from '@enum/TransactionTypeId'
|
||||
import { ContributionCycleType } from '@enum/ContributionCycleType'
|
||||
|
||||
const QueryLinkResult = createUnionType({
|
||||
name: 'QueryLinkResult', // the name of the GraphQL union
|
||||
@ -204,23 +205,60 @@ export class TransactionLinkResolver {
|
||||
throw new Error('Contribution link is depricated')
|
||||
}
|
||||
}
|
||||
if (contributionLink.cycle !== 'ONCE') {
|
||||
logger.error('contribution link has unknown cycle', contributionLink.cycle)
|
||||
throw new Error('Contribution link has unknown cycle')
|
||||
}
|
||||
// Test ONCE rule
|
||||
const alreadyRedeemed = await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.select('contribution')
|
||||
.from(DbContribution, 'contribution')
|
||||
.where('contribution.contributionLinkId = :linkId AND contribution.userId = :id', {
|
||||
linkId: contributionLink.id,
|
||||
id: user.id,
|
||||
})
|
||||
.getOne()
|
||||
if (alreadyRedeemed) {
|
||||
logger.error('contribution link with rule ONCE already redeemed by user with id', user.id)
|
||||
throw new Error('Contribution link already redeemed')
|
||||
let alreadyRedeemed: DbContribution | undefined
|
||||
switch (contributionLink.cycle) {
|
||||
case ContributionCycleType.ONCE: {
|
||||
alreadyRedeemed = await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.select('contribution')
|
||||
.from(DbContribution, 'contribution')
|
||||
.where('contribution.contributionLinkId = :linkId AND contribution.userId = :id', {
|
||||
linkId: contributionLink.id,
|
||||
id: user.id,
|
||||
})
|
||||
.getOne()
|
||||
if (alreadyRedeemed) {
|
||||
logger.error(
|
||||
'contribution link with rule ONCE already redeemed by user with id',
|
||||
user.id,
|
||||
)
|
||||
throw new Error('Contribution link already redeemed')
|
||||
}
|
||||
break
|
||||
}
|
||||
case ContributionCycleType.DAILY: {
|
||||
const start = new Date()
|
||||
start.setHours(0, 0, 0, 0)
|
||||
const end = new Date()
|
||||
end.setHours(23, 59, 59, 999)
|
||||
alreadyRedeemed = await queryRunner.manager
|
||||
.createQueryBuilder()
|
||||
.select('contribution')
|
||||
.from(DbContribution, 'contribution')
|
||||
.where(
|
||||
`contribution.contributionLinkId = :linkId AND contribution.userId = :id
|
||||
AND Date(contribution.confirmedAt) BETWEEN :start AND :end`,
|
||||
{
|
||||
linkId: contributionLink.id,
|
||||
id: user.id,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
)
|
||||
.getOne()
|
||||
if (alreadyRedeemed) {
|
||||
logger.error(
|
||||
'contribution link with rule DAILY already redeemed by user with id',
|
||||
user.id,
|
||||
)
|
||||
throw new Error('Contribution link already redeemed today')
|
||||
}
|
||||
break
|
||||
}
|
||||
default: {
|
||||
logger.error('contribution link has unknown cycle', contributionLink.cycle)
|
||||
throw new Error('Contribution link has unknown cycle')
|
||||
}
|
||||
}
|
||||
|
||||
const creations = await getUserCreation(user.id, false)
|
||||
|
||||
@ -5,6 +5,8 @@ import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/help
|
||||
import { userFactory } from '@/seeds/factory/user'
|
||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||
import {
|
||||
login,
|
||||
logout,
|
||||
createUser,
|
||||
setPassword,
|
||||
forgotPassword,
|
||||
@ -12,7 +14,7 @@ import {
|
||||
createContribution,
|
||||
confirmContribution,
|
||||
} from '@/seeds/graphql/mutations'
|
||||
import { login, logout, verifyLogin, queryOptIn, searchAdminUsers } from '@/seeds/graphql/queries'
|
||||
import { verifyLogin, queryOptIn, searchAdminUsers } from '@/seeds/graphql/queries'
|
||||
import { GraphQLError } from 'graphql'
|
||||
import { User } from '@entity/User'
|
||||
import CONFIG from '@/config'
|
||||
@ -358,7 +360,7 @@ describe('UserResolver', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await userFactory(testEnv, bobBaumeister)
|
||||
await query({ query: login, variables: bobData })
|
||||
await mutate({ mutation: login, variables: bobData })
|
||||
|
||||
// create contribution as user bob
|
||||
contribution = await mutate({
|
||||
@ -367,7 +369,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
// login as admin
|
||||
await query({ query: login, variables: peterData })
|
||||
await mutate({ mutation: login, variables: peterData })
|
||||
|
||||
// confirm the contribution
|
||||
contribution = await mutate({
|
||||
@ -376,7 +378,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
// login as user bob
|
||||
bob = await query({ query: login, variables: bobData })
|
||||
bob = await mutate({ mutation: login, variables: bobData })
|
||||
|
||||
// create transaction link
|
||||
await transactionLinkFactory(testEnv, {
|
||||
@ -582,7 +584,7 @@ describe('UserResolver', () => {
|
||||
describe('no users in database', () => {
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
result = await query({ query: login, variables })
|
||||
result = await mutate({ mutation: login, variables })
|
||||
})
|
||||
|
||||
it('throws an error', () => {
|
||||
@ -603,7 +605,7 @@ describe('UserResolver', () => {
|
||||
describe('user is in database and correct login data', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
result = await query({ query: login, variables })
|
||||
result = await mutate({ mutation: login, variables })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@ -640,7 +642,7 @@ describe('UserResolver', () => {
|
||||
describe('user is in database and wrong password', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
result = await query({ query: login, variables: { ...variables, password: 'wrong' } })
|
||||
result = await mutate({ mutation: login, variables: { ...variables, password: 'wrong' } })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@ -665,7 +667,7 @@ describe('UserResolver', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws an error', async () => {
|
||||
resetToken()
|
||||
await expect(query({ query: logout })).resolves.toEqual(
|
||||
await expect(mutate({ mutation: logout })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [new GraphQLError('401 Unauthorized')],
|
||||
}),
|
||||
@ -681,7 +683,7 @@ describe('UserResolver', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await query({ query: login, variables })
|
||||
await mutate({ mutation: login, variables })
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@ -689,7 +691,7 @@ describe('UserResolver', () => {
|
||||
})
|
||||
|
||||
it('returns true', async () => {
|
||||
await expect(query({ query: logout })).resolves.toEqual(
|
||||
await expect(mutate({ mutation: logout })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { logout: 'true' },
|
||||
errors: undefined,
|
||||
@ -738,7 +740,7 @@ describe('UserResolver', () => {
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
await query({ query: login, variables })
|
||||
await mutate({ mutation: login, variables })
|
||||
user = await User.find()
|
||||
})
|
||||
|
||||
@ -929,8 +931,8 @@ describe('UserResolver', () => {
|
||||
describe('authenticated', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: {
|
||||
email: 'bibi@bloxberg.de',
|
||||
password: 'Aa12345_',
|
||||
@ -1061,8 +1063,8 @@ describe('UserResolver', () => {
|
||||
|
||||
it('can login with new password', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: login,
|
||||
mutate({
|
||||
mutation: login,
|
||||
variables: {
|
||||
email: 'bibi@bloxberg.de',
|
||||
password: 'Bb12345_',
|
||||
@ -1081,8 +1083,8 @@ describe('UserResolver', () => {
|
||||
|
||||
it('cannot login with old password', async () => {
|
||||
await expect(
|
||||
query({
|
||||
query: login,
|
||||
mutate({
|
||||
mutation: login,
|
||||
variables: {
|
||||
email: 'bibi@bloxberg.de',
|
||||
password: 'Aa12345_',
|
||||
@ -1119,8 +1121,8 @@ describe('UserResolver', () => {
|
||||
beforeAll(async () => {
|
||||
await userFactory(testEnv, bibiBloxberg)
|
||||
await userFactory(testEnv, peterLustig)
|
||||
await query({
|
||||
query: login,
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: {
|
||||
email: 'bibi@bloxberg.de',
|
||||
password: 'Aa12345_',
|
||||
|
||||
@ -316,7 +316,7 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.LOGIN])
|
||||
@Query(() => User)
|
||||
@Mutation(() => User)
|
||||
@UseMiddleware(klicktippNewsletterStateMiddleware)
|
||||
async login(
|
||||
@Args() { email, password, publisherId }: UnsecureLoginArgs,
|
||||
@ -377,7 +377,7 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Authorized([RIGHTS.LOGOUT])
|
||||
@Query(() => String)
|
||||
@Mutation(() => String)
|
||||
async logout(): Promise<boolean> {
|
||||
// TODO: We dont need this anymore, but might need this in the future in oder to invalidate a valid JWT-Token.
|
||||
// Furthermore this hook can be useful for tracking user behaviour (did he logout or not? Warn him if he didn't on next login)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { createContributionLink } from '@/seeds/graphql/mutations'
|
||||
import { login } from '@/seeds/graphql/queries'
|
||||
import { login, createContributionLink } from '@/seeds/graphql/mutations'
|
||||
import { ContributionLink } from '@model/ContributionLink'
|
||||
import { ContributionLinkInterface } from '@/seeds/contributionLink/ContributionLinkInterface'
|
||||
|
||||
@ -8,12 +7,12 @@ export const contributionLinkFactory = async (
|
||||
client: ApolloServerTestClient,
|
||||
contributionLink: ContributionLinkInterface,
|
||||
): Promise<ContributionLink> => {
|
||||
const { mutate, query } = client
|
||||
const { mutate } = client
|
||||
|
||||
// login as admin
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const user = await query({
|
||||
query: login,
|
||||
const user = await mutate({
|
||||
mutation: login,
|
||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||
})
|
||||
|
||||
|
||||
@ -2,8 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { backendLogger as logger } from '@/server/logger'
|
||||
import { adminCreateContribution, confirmContribution } from '@/seeds/graphql/mutations'
|
||||
import { login } from '@/seeds/graphql/queries'
|
||||
import { login, adminCreateContribution, confirmContribution } from '@/seeds/graphql/mutations'
|
||||
import { CreationInterface } from '@/seeds/creation/CreationInterface'
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { Transaction } from '@entity/Transaction'
|
||||
@ -19,9 +18,9 @@ export const creationFactory = async (
|
||||
client: ApolloServerTestClient,
|
||||
creation: CreationInterface,
|
||||
): Promise<Contribution | void> => {
|
||||
const { mutate, query } = client
|
||||
const { mutate } = client
|
||||
logger.trace('creationFactory...')
|
||||
await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } })
|
||||
await mutate({ mutation: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } })
|
||||
logger.trace('creationFactory... after login')
|
||||
// TODO it would be nice to have this mutation return the id
|
||||
await mutate({ mutation: adminCreateContribution, variables: { ...creation } })
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ApolloServerTestClient } from 'apollo-server-testing'
|
||||
import { createTransactionLink } from '@/seeds/graphql/mutations'
|
||||
import { login } from '@/seeds/graphql/queries'
|
||||
import { login, createTransactionLink } from '@/seeds/graphql/mutations'
|
||||
import { TransactionLinkInterface } from '@/seeds/transactionLink/TransactionLinkInterface'
|
||||
import { transactionLinkExpireDate } from '@/graphql/resolver/TransactionLinkResolver'
|
||||
import { TransactionLink } from '@entity/TransactionLink'
|
||||
@ -9,10 +8,13 @@ export const transactionLinkFactory = async (
|
||||
client: ApolloServerTestClient,
|
||||
transactionLink: TransactionLinkInterface,
|
||||
): Promise<void> => {
|
||||
const { mutate, query } = client
|
||||
const { mutate } = client
|
||||
|
||||
// login
|
||||
await query({ query: login, variables: { email: transactionLink.email, password: 'Aa12345_' } })
|
||||
await mutate({
|
||||
mutation: login,
|
||||
variables: { email: transactionLink.email, password: 'Aa12345_' },
|
||||
})
|
||||
|
||||
const variables = {
|
||||
amount: transactionLink.amount,
|
||||
|
||||
@ -289,3 +289,33 @@ export const adminCreateContributionMessage = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const redeemTransactionLink = gql`
|
||||
mutation ($code: String!) {
|
||||
redeemTransactionLink(code: $code)
|
||||
}
|
||||
`
|
||||
|
||||
export const login = gql`
|
||||
mutation ($email: String!, $password: String!, $publisherId: Int) {
|
||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||
id
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
language
|
||||
klickTipp {
|
||||
newsletterState
|
||||
}
|
||||
hasElopage
|
||||
publisherId
|
||||
isAdmin
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const logout = gql`
|
||||
mutation {
|
||||
logout
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,23 +1,5 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const login = gql`
|
||||
query ($email: String!, $password: String!, $publisherId: Int) {
|
||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||
id
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
language
|
||||
klickTipp {
|
||||
newsletterState
|
||||
}
|
||||
hasElopage
|
||||
publisherId
|
||||
isAdmin
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const verifyLogin = gql`
|
||||
query {
|
||||
verifyLogin {
|
||||
@ -35,12 +17,6 @@ export const verifyLogin = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const logout = gql`
|
||||
query {
|
||||
logout
|
||||
}
|
||||
`
|
||||
|
||||
export const queryOptIn = gql`
|
||||
query ($optIn: String!) {
|
||||
queryOptIn(optIn: $optIn)
|
||||
|
||||
33
backend/test/extensions.ts
Normal file
33
backend/test/extensions.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
|
||||
import Decimal from 'decimal.js-light'
|
||||
|
||||
expect.extend({
|
||||
decimalEqual(received, value) {
|
||||
const pass = new Decimal(value).equals(received.toString())
|
||||
if (pass) {
|
||||
return {
|
||||
message: () => `expected ${received} to not equal ${value}`,
|
||||
pass: true,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
message: () => `expected ${received} to equal ${value}`,
|
||||
pass: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
interface CustomMatchers<R = unknown> {
|
||||
decimalEqual(value: number): R
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace jest {
|
||||
interface Expect extends CustomMatchers {}
|
||||
interface Matchers<R> extends CustomMatchers<R> {}
|
||||
interface InverseAsymmetricMatchers extends CustomMatchers {}
|
||||
}
|
||||
}
|
||||
@ -1668,9 +1668,9 @@ camelcase@^6.2.0:
|
||||
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
|
||||
|
||||
caniuse-lite@^1.0.30001264:
|
||||
version "1.0.30001325"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz"
|
||||
integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==
|
||||
version "1.0.30001418"
|
||||
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz"
|
||||
integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==
|
||||
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
|
||||
@ -62,13 +62,13 @@ The business events will be stored in database in the new table `EventProtocol`.
|
||||
|
||||
The following table lists for each event type the mapping between old and new key, the mandatory attributes, which have to be initialized at event occurence and to be written in the database event protocol table:
|
||||
|
||||
| EventType - old key | EventType - new key | id | type | createdAt | userID | XuserID | XCommunityID | transactionID | contribID | amount |
|
||||
| EventKey | EventType | id | type | createdAt | userID | XuserID | XCommunityID | transactionID | contribID | amount |
|
||||
| :-------------------------------- | :------------------------------------- | :-: | :--: | :-------: | :----: | :-----: | :----------: | :-----------: | :-------: | :----: |
|
||||
| BASIC | BasicEvent | x | x | x | | | | | | |
|
||||
| VISIT_GRADIDO | VisitGradidoEvent | x | x | x | | | | | | |
|
||||
| REGISTER | RegisterEvent | x | x | x | x | | | | | |
|
||||
| LOGIN | LoginEvent | x | x | x | x | | | | | |
|
||||
| | VerifyRedeemEvent | | | | | | | | | |
|
||||
| VERIFY_REDEEM | VerifyRedeemEvent | x | x | x | x | | | (x) | (x) | |
|
||||
| REDEEM_REGISTER | RedeemRegisterEvent | x | x | x | x | | | (x) | (x) | |
|
||||
| REDEEM_LOGIN | RedeemLoginEvent | x | x | x | x | | | (x) | (x) | |
|
||||
| ACTIVATE_ACCOUNT | ActivateAccountEvent | x | x | x | x | | | | | |
|
||||
@ -82,20 +82,20 @@ The following table lists for each event type the mapping between old and new ke
|
||||
| TRANSACTION_SEND_REDEEM | TransactionLinkRedeemEvent | x | x | x | x | x | x | x | | x |
|
||||
| CONTRIBUTION_CREATE | ContributionCreateEvent | x | x | x | x | | | | x | x |
|
||||
| CONTRIBUTION_CONFIRM | ContributionConfirmEvent | x | x | x | x | x | x | | x | x |
|
||||
| | ContributionDenyEvent | x | x | x | x | x | x | | x | x |
|
||||
| CONTRIBUTION_DENY | ContributionDenyEvent | x | x | x | x | x | x | | x | x |
|
||||
| CONTRIBUTION_LINK_DEFINE | ContributionLinkDefineEvent | x | x | x | x | | | | | x |
|
||||
| CONTRIBUTION_LINK_ACTIVATE_REDEEM | ContributionLinkRedeemEvent | x | x | x | x | | | | x | x |
|
||||
| | UserCreateContributionMessageEvent | x | x | x | x | | | | x | x |
|
||||
| | AdminCreateContributionMessageEvent | x | x | x | x | | | | x | x |
|
||||
| | LogoutEvent | x | x | x | x | | | | x | x |
|
||||
| USER_CREATES_CONTRIBUTION_MESSAGE | UserCreateContributionMessageEvent | x | x | x | x | | | | x | x |
|
||||
| ADMIN_CREATES_CONTRIBUTION_MESSAGE | AdminCreateContributionMessageEvent | x | x | x | x | | | | x | x |
|
||||
| LOGOUT | LogoutEvent | x | x | x | x | | | | | |
|
||||
| SEND_CONFIRMATION_EMAIL | SendConfirmEmailEvent | x | x | x | x | | | | | |
|
||||
| | SendAccountMultiRegistrationEmailEvent | x | x | x | x | | | | | |
|
||||
| | SendForgotPasswordEmailEvent | x | x | x | x | | | | | |
|
||||
| | SendTransactionSendEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| | SendTransactionReceiveEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| | SendAddedContributionEmailEvent | x | x | x | x | | | | x | x |
|
||||
| | SendContributionConfirmEmailEvent | x | x | x | x | | | | x | x |
|
||||
| | SendTransactionLinkRedeemEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| SEND_ACCOUNT_MULTIREGISTRATION_EMAIL | SendAccountMultiRegistrationEmailEvent | x | x | x | x | | | | | |
|
||||
| SEND_FORGOT_PASSWORD_EMAIL | SendForgotPasswordEmailEvent | x | x | x | x | | | | | |
|
||||
| SEND_TRANSACTION_SEND_EMAIL | SendTransactionSendEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| SEND_TRANSACTION_RECEIVE_EMAIL | SendTransactionReceiveEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| SEND_ADDED_CONTRIBUTION_EMAIL | SendAddedContributionEmailEvent | x | x | x | x | | | | x | x |
|
||||
| SEND_CONTRIBUTION_CONFIRM_EMAIL | SendContributionConfirmEmailEvent | x | x | x | x | | | | x | x |
|
||||
| SEND_TRANSACTION_LINK_REDEEM_EMAIL | SendTransactionLinkRedeemEmailEvent | x | x | x | x | x | x | x | | x |
|
||||
| TRANSACTION_REPEATE_REDEEM | - | | | | | | | | | |
|
||||
| TRANSACTION_RECEIVE_REDEEM | - | | | | | | | | | |
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ describe('App', () => {
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
},
|
||||
params: {},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -18,9 +18,9 @@
|
||||
<b-img class="sheet-img position-absolute d-block d-lg-none zindex1000" :src="sheet"></b-img>
|
||||
<b-collapse id="nav-collapse" is-nav class="ml-5">
|
||||
<b-navbar-nav class="ml-auto d-none d-lg-flex" right>
|
||||
<b-nav-item to="/register" class="authNavbar ml-lg-5">{{ $t('signup') }}</b-nav-item>
|
||||
<b-nav-item :to="register" class="authNavbar ml-lg-5">{{ $t('signup') }}</b-nav-item>
|
||||
<span class="d-none d-lg-block mt-1">{{ $t('math.pipe') }}</span>
|
||||
<b-nav-item to="/login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
|
||||
<b-nav-item :to="login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
@ -28,8 +28,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { authLinks } from '@/mixins/authLinks'
|
||||
|
||||
export default {
|
||||
name: 'AuthNavbar',
|
||||
mixins: [authLinks],
|
||||
data() {
|
||||
return {
|
||||
logo: '/img/brand/green.png',
|
||||
|
||||
@ -2,17 +2,20 @@
|
||||
<div class="navbar-small">
|
||||
<b-navbar class="navi">
|
||||
<b-navbar-nav>
|
||||
<b-nav-item to="/register" class="authNavbar">{{ $t('signup') }}</b-nav-item>
|
||||
<b-nav-item :to="register" class="authNavbar">{{ $t('signup') }}</b-nav-item>
|
||||
<span class="mt-1">{{ $t('math.pipe') }}</span>
|
||||
<b-nav-item to="/login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
|
||||
<b-nav-item :to="login" class="authNavbar">{{ $t('signin') }}</b-nav-item>
|
||||
</b-navbar-nav>
|
||||
</b-navbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { authLinks } from '@/mixins/authLinks'
|
||||
|
||||
export default {
|
||||
name: 'AuthNavbarSmall',
|
||||
mixins: [authLinks],
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
@ -329,7 +329,8 @@ describe('ContributionForm', () => {
|
||||
|
||||
describe('invalid form data', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
// skip this precondition as long as the datepicker is disabled in the component
|
||||
// await wrapper.findComponent({ name: 'BFormDatepicker' }).vm.$emit('input', now)
|
||||
await wrapper.find('#contribution-amount').find('input').setValue('200')
|
||||
})
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
reset-value=""
|
||||
:label-no-date-selected="$t('contribution.noDateSelected')"
|
||||
required
|
||||
:disabled="this.form.id !== null"
|
||||
>
|
||||
<template #nav-prev-year><span></span></template>
|
||||
<template #nav-next-year><span></span></template>
|
||||
|
||||
@ -25,9 +25,11 @@
|
||||
</template>
|
||||
<script>
|
||||
import RedeemInformation from '@/components/LinkInformations/RedeemInformation.vue'
|
||||
import { authLinks } from '@/mixins/authLinks'
|
||||
|
||||
export default {
|
||||
name: 'RedeemLoggedOut',
|
||||
mixins: [authLinks],
|
||||
components: {
|
||||
RedeemInformation,
|
||||
},
|
||||
@ -35,13 +37,5 @@ export default {
|
||||
linkData: { type: Object, required: true },
|
||||
isContributionLink: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
login() {
|
||||
return '/login/' + this.$route.params.code
|
||||
},
|
||||
register() {
|
||||
return '/register/' + this.$route.params.code
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -136,3 +136,27 @@ export const createContributionMessage = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const login = gql`
|
||||
mutation($email: String!, $password: String!, $publisherId: Int) {
|
||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
language
|
||||
klickTipp {
|
||||
newsletterState
|
||||
}
|
||||
hasElopage
|
||||
publisherId
|
||||
isAdmin
|
||||
creation
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const logout = gql`
|
||||
mutation {
|
||||
logout
|
||||
}
|
||||
`
|
||||
|
||||
@ -1,23 +1,5 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const login = gql`
|
||||
query($email: String!, $password: String!, $publisherId: Int) {
|
||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||
email
|
||||
firstName
|
||||
lastName
|
||||
language
|
||||
klickTipp {
|
||||
newsletterState
|
||||
}
|
||||
hasElopage
|
||||
publisherId
|
||||
isAdmin
|
||||
creation
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const verifyLogin = gql`
|
||||
query {
|
||||
verifyLogin {
|
||||
@ -36,12 +18,6 @@ export const verifyLogin = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const logout = gql`
|
||||
query {
|
||||
logout
|
||||
}
|
||||
`
|
||||
|
||||
export const transactionsQuery = gql`
|
||||
query($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC) {
|
||||
transactionList(currentPage: $currentPage, pageSize: $pageSize, order: $order) {
|
||||
|
||||
@ -19,6 +19,7 @@ describe('AuthLayout', () => {
|
||||
meta: {
|
||||
requiresAuth: false,
|
||||
},
|
||||
params: {},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ const apolloMock = jest.fn().mockResolvedValue({
|
||||
logout: 'success',
|
||||
},
|
||||
})
|
||||
const apolloQueryMock = jest.fn()
|
||||
|
||||
describe('DashboardLayout', () => {
|
||||
let wrapper
|
||||
@ -40,7 +41,8 @@ describe('DashboardLayout', () => {
|
||||
},
|
||||
},
|
||||
$apollo: {
|
||||
query: apolloMock,
|
||||
mutate: apolloMock,
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
@ -142,7 +144,7 @@ describe('DashboardLayout', () => {
|
||||
|
||||
describe('update transactions', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMock.mockResolvedValue({
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
transactionList: {
|
||||
balance: {
|
||||
@ -163,7 +165,7 @@ describe('DashboardLayout', () => {
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMock).toBeCalledWith(
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
currentPage: 2,
|
||||
@ -201,7 +203,7 @@ describe('DashboardLayout', () => {
|
||||
|
||||
describe('update transactions returns error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMock.mockRejectedValue({
|
||||
apolloQueryMock.mockRejectedValue({
|
||||
message: 'Ouch!',
|
||||
})
|
||||
await wrapper
|
||||
|
||||
@ -41,7 +41,8 @@
|
||||
import Navbar from '@/components/Menu/Navbar.vue'
|
||||
import Sidebar from '@/components/Menu/Sidebar.vue'
|
||||
import SessionLogoutTimeout from '@/components/SessionLogoutTimeout.vue'
|
||||
import { logout, transactionsQuery } from '@/graphql/queries'
|
||||
import { transactionsQuery } from '@/graphql/queries'
|
||||
import { logout } from '@/graphql/mutations'
|
||||
import ContentFooter from '@/components/ContentFooter.vue'
|
||||
import { FadeTransition } from 'vue2-transitions'
|
||||
import CONFIG from '@/config'
|
||||
@ -75,8 +76,8 @@ export default {
|
||||
methods: {
|
||||
async logout() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: logout,
|
||||
.mutate({
|
||||
mutation: logout,
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.dispatch('logout')
|
||||
|
||||
@ -24,7 +24,8 @@
|
||||
"moderator": "Moderator",
|
||||
"moderators": "Moderatoren",
|
||||
"myContributions": "Meine Beiträge zum Gemeinwohl",
|
||||
"openContributionLinks": "öffentliche Beitrags-Linkliste",
|
||||
"noOpenContributionLinkText": "Zur Zeit gibt es keine automatischen Schöpfungen.",
|
||||
"openContributionLinks": "Öffentliche Beitrags-Linkliste",
|
||||
"openContributionLinkText": "Folgende {count} automatische Schöpfungen werden zur Zeit durch die Gemeinschaft „{name}“ bereitgestellt.",
|
||||
"other-communities": "Weitere Gemeinschaften",
|
||||
"submitContribution": "Beitrag einreichen",
|
||||
|
||||
@ -24,7 +24,8 @@
|
||||
"moderator": "Moderator",
|
||||
"moderators": "Moderators",
|
||||
"myContributions": "My contributions to the common good",
|
||||
"openContributionLinks": "open Contribution links list",
|
||||
"noOpenContributionLinkText": "Currently there are no automatic creations.",
|
||||
"openContributionLinks": "Open contribution-link list",
|
||||
"openContributionLinkText": "The following {count} automatic creations are currently provided by the \"{name}\" community.",
|
||||
"other-communities": "Other communities",
|
||||
"submitContribution": "Submit contribution",
|
||||
|
||||
12
frontend/src/mixins/authLinks.js
Normal file
12
frontend/src/mixins/authLinks.js
Normal file
@ -0,0 +1,12 @@
|
||||
export const authLinks = {
|
||||
computed: {
|
||||
login() {
|
||||
if (this.$route.params.code) return '/login/' + this.$route.params.code
|
||||
return '/login'
|
||||
},
|
||||
register() {
|
||||
if (this.$route.params.code) return '/register/' + this.$route.params.code
|
||||
return '/register'
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -14,7 +14,7 @@
|
||||
<hr />
|
||||
<b-container>
|
||||
<div class="h3">{{ $t('community.openContributionLinks') }}</div>
|
||||
<small>
|
||||
<small v-if="count > 0">
|
||||
{{
|
||||
$t('community.openContributionLinkText', {
|
||||
name: CONFIG.COMMUNITY_NAME,
|
||||
@ -22,6 +22,9 @@
|
||||
})
|
||||
}}
|
||||
</small>
|
||||
<small v-else>
|
||||
{{ $t('community.noOpenContributionLinkText') }}
|
||||
</small>
|
||||
<ul>
|
||||
<li v-for="item in itemsContributionLinks" v-bind:key="item.id">
|
||||
<div>{{ item.name }}</div>
|
||||
|
||||
@ -5,7 +5,7 @@ import Login from './Login'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn()
|
||||
const apolloMutateMock = jest.fn()
|
||||
const mockStoreDispach = jest.fn()
|
||||
const mockStoreCommit = jest.fn()
|
||||
const mockRouterPush = jest.fn()
|
||||
@ -41,7 +41,7 @@ describe('Login', () => {
|
||||
params: {},
|
||||
},
|
||||
$apollo: {
|
||||
query: apolloQueryMock,
|
||||
mutate: apolloMutateMock,
|
||||
},
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ describe('Login', () => {
|
||||
await wrapper.find('input[placeholder="Email"]').setValue('user@example.org')
|
||||
await wrapper.find('input[placeholder="form.password"]').setValue('1234')
|
||||
await flushPromises()
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
apolloMutateMock.mockResolvedValue({
|
||||
data: {
|
||||
login: 'token',
|
||||
},
|
||||
@ -123,7 +123,7 @@ describe('Login', () => {
|
||||
})
|
||||
|
||||
it('calls the API with the given data', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'user@example.org',
|
||||
@ -175,7 +175,7 @@ describe('Login', () => {
|
||||
|
||||
describe('login fails', () => {
|
||||
const createError = async (errorMessage) => {
|
||||
apolloQueryMock.mockRejectedValue({
|
||||
apolloMutateMock.mockRejectedValue({
|
||||
message: errorMessage,
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
import InputPassword from '@/components/Inputs/InputPassword'
|
||||
import InputEmail from '@/components/Inputs/InputEmail'
|
||||
import Message from '@/components/Message/Message'
|
||||
import { login } from '@/graphql/queries'
|
||||
import { login } from '@/graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'Login',
|
||||
@ -71,14 +71,13 @@ export default {
|
||||
container: this.$refs.submitButton,
|
||||
})
|
||||
this.$apollo
|
||||
.query({
|
||||
query: login,
|
||||
.mutate({
|
||||
mutation: login,
|
||||
variables: {
|
||||
email: this.form.email,
|
||||
password: this.form.password,
|
||||
publisherId: this.$store.state.publisherId,
|
||||
},
|
||||
fetchPolicy: 'network-only',
|
||||
})
|
||||
.then(async (result) => {
|
||||
const {
|
||||
|
||||
@ -43,6 +43,7 @@ const mocks = {
|
||||
$store: {
|
||||
state: {
|
||||
token: null,
|
||||
tokenTime: null,
|
||||
email: 'bibi@bloxberg.de',
|
||||
},
|
||||
},
|
||||
@ -68,7 +69,7 @@ describe('TransactionLink', () => {
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
beforeEach(() => {
|
||||
beforeAll(() => {
|
||||
jest.clearAllMocks()
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
@ -214,112 +215,159 @@ describe('TransactionLink', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('token in store and own link', () => {
|
||||
beforeEach(() => {
|
||||
describe('token in store', () => {
|
||||
beforeAll(() => {
|
||||
mocks.$store.state.token = 'token'
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
queryTransactionLink: {
|
||||
__typename: 'TransactionLink',
|
||||
id: 92,
|
||||
amount: '22',
|
||||
memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
|
||||
createdAt: '2022-03-17T16:10:28.000Z',
|
||||
validUntil: transactionLinkValidExpireDate(),
|
||||
redeemedAt: null,
|
||||
deletedAt: null,
|
||||
user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a RedeemSelfCreator component', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has a no redeem text', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).text()).toContain(
|
||||
'gdd_per_link.no-redeem',
|
||||
)
|
||||
})
|
||||
|
||||
it.skip('has a link to transaction page', () => {
|
||||
expect(wrapper.find('a[target="/transactions"]').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('valid link', () => {
|
||||
beforeEach(() => {
|
||||
mocks.$store.state.token = 'token'
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
queryTransactionLink: {
|
||||
__typename: 'TransactionLink',
|
||||
id: 92,
|
||||
amount: '22',
|
||||
memo: 'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
|
||||
createdAt: '2022-03-17T16:10:28.000Z',
|
||||
validUntil: transactionLinkValidExpireDate(),
|
||||
redeemedAt: null,
|
||||
deletedAt: null,
|
||||
user: { firstName: 'Peter', publisherId: 0, email: 'peter@listig.de' },
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a RedeemValid component', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemValid' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has a button with redeem text', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemValid' }).find('button').text()).toBe(
|
||||
'gdd_per_link.redeem',
|
||||
)
|
||||
})
|
||||
|
||||
describe('redeem link with success', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockResolvedValue()
|
||||
await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
|
||||
describe('sufficient token time in store', () => {
|
||||
beforeAll(() => {
|
||||
mocks.$store.state.tokenTime = Math.floor(Date.now() / 1000) + 20
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'some-code',
|
||||
describe('own link', () => {
|
||||
beforeAll(() => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
queryTransactionLink: {
|
||||
__typename: 'TransactionLink',
|
||||
id: 92,
|
||||
amount: '22',
|
||||
memo:
|
||||
'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
|
||||
createdAt: '2022-03-17T16:10:28.000Z',
|
||||
validUntil: transactionLinkValidExpireDate(),
|
||||
redeemedAt: null,
|
||||
deletedAt: null,
|
||||
user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('has a RedeemSelfCreator component', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has a no redeem text', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemSelfCreator' }).text()).toContain(
|
||||
'gdd_per_link.no-redeem',
|
||||
)
|
||||
})
|
||||
|
||||
it.skip('has a link to transaction page', () => {
|
||||
expect(wrapper.find('a[target="/transactions"]').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(mocks.$t).toBeCalledWith('gdd_per_link.redeem')
|
||||
expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ')
|
||||
})
|
||||
describe('valid link', () => {
|
||||
beforeAll(() => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
queryTransactionLink: {
|
||||
__typename: 'TransactionLink',
|
||||
id: 92,
|
||||
amount: '22',
|
||||
memo:
|
||||
'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
|
||||
createdAt: '2022-03-17T16:10:28.000Z',
|
||||
validUntil: transactionLinkValidExpireDate(),
|
||||
redeemedAt: null,
|
||||
deletedAt: null,
|
||||
user: { firstName: 'Peter', publisherId: 0, email: 'peter@listig.de' },
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('pushes the route to overview', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/overview')
|
||||
it('has a RedeemValid component', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemValid' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has a button with redeem text', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemValid' }).find('button').text()).toBe(
|
||||
'gdd_per_link.redeem',
|
||||
)
|
||||
})
|
||||
|
||||
describe('redeem link with success', () => {
|
||||
beforeAll(async () => {
|
||||
apolloMutateMock.mockResolvedValue()
|
||||
await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('calls the API', () => {
|
||||
expect(apolloMutateMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
mutation: redeemTransactionLink,
|
||||
variables: {
|
||||
code: 'some-code',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(mocks.$t).toBeCalledWith('gdd_per_link.redeem')
|
||||
expect(toastSuccessSpy).toBeCalledWith('gdd_per_link.redeemed; ')
|
||||
})
|
||||
|
||||
it('pushes the route to overview', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/overview')
|
||||
})
|
||||
})
|
||||
|
||||
describe('redeem link with error', () => {
|
||||
beforeAll(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' })
|
||||
await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh Noo!')
|
||||
})
|
||||
|
||||
it('pushes the route to overview', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/overview')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('redeem link with error', () => {
|
||||
beforeEach(async () => {
|
||||
apolloMutateMock.mockRejectedValue({ message: 'Oh Noo!' })
|
||||
await wrapper.findComponent({ name: 'RedeemValid' }).find('button').trigger('click')
|
||||
describe('no sufficient token time in store', () => {
|
||||
beforeAll(() => {
|
||||
mocks.$store.state.tokenTime = 1665125185
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
data: {
|
||||
queryTransactionLink: {
|
||||
__typename: 'TransactionLink',
|
||||
id: 92,
|
||||
amount: '22',
|
||||
memo:
|
||||
'Abrakadabra drei, vier, fünf, sechs, hier steht jetzt ein Memotext! Hex hex ',
|
||||
createdAt: '2022-03-17T16:10:28.000Z',
|
||||
validUntil: transactionLinkValidExpireDate(),
|
||||
redeemedAt: null,
|
||||
deletedAt: null,
|
||||
user: { firstName: 'Bibi', publisherId: 0, email: 'bibi@bloxberg.de' },
|
||||
},
|
||||
},
|
||||
})
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('toasts an error message', () => {
|
||||
expect(toastErrorSpy).toBeCalledWith('Oh Noo!')
|
||||
it('has a RedeemLoggedOut component', () => {
|
||||
expect(wrapper.findComponent({ name: 'RedeemLoggedOut' }).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('pushes the route to overview', () => {
|
||||
expect(routerPushMock).toBeCalledWith('/overview')
|
||||
it('has a link to register with code', () => {
|
||||
expect(wrapper.find('a[href="/register/some-code"]').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('has a link to login with code', () => {
|
||||
expect(wrapper.find('a[href="/login/some-code"]').exists()).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -103,6 +103,12 @@ export default {
|
||||
isContributionLink() {
|
||||
return this.$route.params.code.search(/^CL-/) === 0
|
||||
},
|
||||
tokenExpiresInSeconds() {
|
||||
const remainingSecs = Math.floor(
|
||||
(new Date(this.$store.state.tokenTime * 1000).getTime() - new Date().getTime()) / 1000,
|
||||
)
|
||||
return remainingSecs <= 0 ? 0 : remainingSecs
|
||||
},
|
||||
itemType() {
|
||||
// link is deleted: at, from
|
||||
if (this.linkData.deletedAt) {
|
||||
@ -130,7 +136,9 @@ export default {
|
||||
return `TEXT`
|
||||
}
|
||||
|
||||
if (this.$store.state.token) {
|
||||
if (this.$store.state.token && this.$store.state.tokenTime) {
|
||||
if (this.tokenExpiresInSeconds < 5) return `LOGGED_OUT`
|
||||
|
||||
// logged in, nicht berechtigt einzulösen, eigener link
|
||||
if (this.linkData.user && this.$store.state.email === this.linkData.user.email) {
|
||||
return `SELF_CREATOR`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user