mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into event_protocol_rework
This commit is contained in:
commit
70c6d3b026
19
.github/workflows/test.yml
vendored
19
.github/workflows/test.yml
vendored
@ -360,6 +360,25 @@ jobs:
|
|||||||
- name: backend | Lint
|
- name: backend | Lint
|
||||||
run: docker run --rm gradido/backend:test yarn run lint
|
run: docker run --rm gradido/backend:test yarn run lint
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# JOB: LOCALES BACKEND #######################################################
|
||||||
|
##############################################################################
|
||||||
|
locales_backend:
|
||||||
|
name: Locales - Backend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build_test_backend]
|
||||||
|
steps:
|
||||||
|
##########################################################################
|
||||||
|
# CHECKOUT CODE ##########################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
##########################################################################
|
||||||
|
# LOCALES BACKEND #####################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Backend | Locales
|
||||||
|
run: cd backend && yarn && yarn locales
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT DATABASE UP ######################################################
|
# JOB: LINT DATABASE UP ######################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|||||||
@ -42,14 +42,30 @@ describe('ContributionLink', () => {
|
|||||||
expect(wrapper.find('div.contribution-link').exists()).toBe(true)
|
expect(wrapper.find('div.contribution-link').exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits toggle::collapse new Contribution', async () => {
|
describe('function editContributionLinkData', () => {
|
||||||
wrapper.vm.editContributionLinkData()
|
beforeEach(() => {
|
||||||
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
wrapper.vm.editContributionLinkData()
|
||||||
|
})
|
||||||
|
it('emits toggle::collapse new Contribution', async () => {
|
||||||
|
await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('emits toggle::collapse close Contribution-Form ', async () => {
|
describe('function closeContributionForm', () => {
|
||||||
wrapper.vm.closeContributionForm()
|
beforeEach(async () => {
|
||||||
expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
await wrapper.setData({ visible: true })
|
||||||
|
wrapper.vm.closeContributionForm()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('emits toggle::collapse close Contribution-Form ', async () => {
|
||||||
|
await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy()
|
||||||
|
})
|
||||||
|
it('editContributionLink is false', async () => {
|
||||||
|
await expect(wrapper.vm.editContributionLink).toBe(false)
|
||||||
|
})
|
||||||
|
it('contributionLinkData is empty', async () => {
|
||||||
|
await expect(wrapper.vm.contributionLinkData).toEqual({})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -88,5 +88,16 @@ describe('CreationTransactionList', () => {
|
|||||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('watch currentPage', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await wrapper.setData({ currentPage: 2 })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns the string in normal order if reversed property is not true', () => {
|
||||||
|
expect(wrapper.vm.currentPage).toBe(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -46,39 +46,31 @@ describe('NavBar', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('Navbar Menu', () => {
|
describe('Navbar Menu', () => {
|
||||||
it('has a link to overview', () => {
|
|
||||||
expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has a link to /user', () => {
|
it('has a link to /user', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe('/user')
|
expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/user')
|
||||||
})
|
|
||||||
|
|
||||||
it('has a link to /creation', () => {
|
|
||||||
expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe('/creation')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /creation-confirm', () => {
|
it('has a link to /creation-confirm', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe(
|
expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe(
|
||||||
'/creation-confirm',
|
'/creation-confirm',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /contribution-links', () => {
|
it('has a link to /contribution-links', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe(
|
expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe(
|
||||||
'/contribution-links',
|
'/contribution-links',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a link to /statistic', () => {
|
it('has a link to /statistic', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('/statistic')
|
expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('wallet', () => {
|
describe('wallet', () => {
|
||||||
const assignLocationSpy = jest.fn()
|
const assignLocationSpy = jest.fn()
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await wrapper.findAll('.nav-item').at(6).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('changes window location to wallet', () => {
|
it.skip('changes window location to wallet', () => {
|
||||||
@ -97,7 +89,7 @@ describe('NavBar', () => {
|
|||||||
window.location = {
|
window.location = {
|
||||||
assign: windowLocationMock,
|
assign: windowLocationMock,
|
||||||
}
|
}
|
||||||
await wrapper.findAll('.nav-item').at(7).find('a').trigger('click')
|
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('redirects to /logout', () => {
|
it('redirects to /logout', () => {
|
||||||
|
|||||||
@ -9,9 +9,7 @@
|
|||||||
|
|
||||||
<b-collapse id="nav-collapse" is-nav>
|
<b-collapse id="nav-collapse" is-nav>
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
<b-nav-item to="/">{{ $t('navbar.overview') }}</b-nav-item>
|
|
||||||
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
|
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
|
||||||
<b-nav-item to="/creation">{{ $t('navbar.multi_creation') }}</b-nav-item>
|
|
||||||
<b-nav-item
|
<b-nav-item
|
||||||
v-show="$store.state.openCreations > 0"
|
v-show="$store.state.openCreations > 0"
|
||||||
class="bg-color-creation p-1"
|
class="bg-color-creation p-1"
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="component-select-users-table">
|
|
||||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
|
||||||
<template #cell(bookmark)="row">
|
|
||||||
<div>
|
|
||||||
<b-button
|
|
||||||
v-if="row.item.emailChecked"
|
|
||||||
variant="warning"
|
|
||||||
size="md"
|
|
||||||
@click="$emit('push-item', row.item)"
|
|
||||||
class="mr-2"
|
|
||||||
>
|
|
||||||
<b-icon icon="plus" variant="success"></b-icon>
|
|
||||||
</b-button>
|
|
||||||
<div v-else>{{ $t('e_mail') }}{{ $t('math.exclaim') }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</b-table-lite>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'SelectUsersTable',
|
|
||||||
props: {
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="component-selected-users-table">
|
|
||||||
<b-table-lite :items="items" :fields="fields" caption-top striped hover stacked="md">
|
|
||||||
<template #cell(bookmark)="row">
|
|
||||||
<b-button variant="danger" size="md" @click="$emit('remove-item', row.item)" class="mr-2">
|
|
||||||
<b-icon icon="x" variant="light"></b-icon>
|
|
||||||
</b-button>
|
|
||||||
</template>
|
|
||||||
</b-table-lite>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'SelectedUsersTable',
|
|
||||||
props: {
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
fields: {
|
|
||||||
type: Array,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -32,7 +32,6 @@
|
|||||||
"creation": "Schöpfung",
|
"creation": "Schöpfung",
|
||||||
"creationList": "Schöpfungsliste",
|
"creationList": "Schöpfungsliste",
|
||||||
"creation_form": {
|
"creation_form": {
|
||||||
"creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.",
|
|
||||||
"creation_for": "Aktives Grundeinkommen für",
|
"creation_for": "Aktives Grundeinkommen für",
|
||||||
"enter_text": "Text eintragen",
|
"enter_text": "Text eintragen",
|
||||||
"form": "Schöpfungsformular",
|
"form": "Schöpfungsformular",
|
||||||
@ -87,7 +86,6 @@
|
|||||||
"lastname": "Nachname",
|
"lastname": "Nachname",
|
||||||
"math": {
|
"math": {
|
||||||
"equals": "=",
|
"equals": "=",
|
||||||
"exclaim": "!",
|
|
||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
@ -95,15 +93,12 @@
|
|||||||
"request": "Die Anfrage wurde gesendet."
|
"request": "Die Anfrage wurde gesendet."
|
||||||
},
|
},
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatische Beiträge",
|
"automaticContributions": "Automatische Beiträge",
|
||||||
"logout": "Abmelden",
|
"logout": "Abmelden",
|
||||||
"multi_creation": "Mehrfachschöpfung",
|
|
||||||
"my-account": "Mein Konto",
|
"my-account": "Mein Konto",
|
||||||
"open_creation": "Offene Schöpfungen",
|
"open_creation": "Offene Schöpfungen",
|
||||||
"overview": "Übersicht",
|
|
||||||
"statistic": "Statistik",
|
"statistic": "Statistik",
|
||||||
"user_search": "Nutzersuche"
|
"user_search": "Nutzersuche"
|
||||||
},
|
},
|
||||||
@ -132,9 +127,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redeemed": "eingelöst",
|
"redeemed": "eingelöst",
|
||||||
"remove": "Entfernen",
|
|
||||||
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
"removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.",
|
||||||
"remove_all": "alle Nutzer entfernen",
|
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"statistic": {
|
"statistic": {
|
||||||
"activeUsers": "Aktive Mitglieder",
|
"activeUsers": "Aktive Mitglieder",
|
||||||
|
|||||||
@ -32,7 +32,6 @@
|
|||||||
"creation": "Creation",
|
"creation": "Creation",
|
||||||
"creationList": "Creation list",
|
"creationList": "Creation list",
|
||||||
"creation_form": {
|
"creation_form": {
|
||||||
"creation_failed": "Could not create pending creation for {email}",
|
|
||||||
"creation_for": "Active Basic Income for",
|
"creation_for": "Active Basic Income for",
|
||||||
"enter_text": "Enter text",
|
"enter_text": "Enter text",
|
||||||
"form": "Creation form",
|
"form": "Creation form",
|
||||||
@ -87,7 +86,6 @@
|
|||||||
"lastname": "Lastname",
|
"lastname": "Lastname",
|
||||||
"math": {
|
"math": {
|
||||||
"equals": "=",
|
"equals": "=",
|
||||||
"exclaim": "!",
|
|
||||||
"pipe": "|",
|
"pipe": "|",
|
||||||
"plus": "+"
|
"plus": "+"
|
||||||
},
|
},
|
||||||
@ -95,15 +93,12 @@
|
|||||||
"request": "Request has been sent."
|
"request": "Request has been sent."
|
||||||
},
|
},
|
||||||
"moderator": "Moderator",
|
"moderator": "Moderator",
|
||||||
"multiple_creation_text": "Please select one or more members for which you would like to perform creations.",
|
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"navbar": {
|
"navbar": {
|
||||||
"automaticContributions": "Automatic Contributions",
|
"automaticContributions": "Automatic Contributions",
|
||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"multi_creation": "Multiple creation",
|
|
||||||
"my-account": "My Account",
|
"my-account": "My Account",
|
||||||
"open_creation": "Open creations",
|
"open_creation": "Open creations",
|
||||||
"overview": "Overview",
|
|
||||||
"statistic": "Statistic",
|
"statistic": "Statistic",
|
||||||
"user_search": "User search"
|
"user_search": "User search"
|
||||||
},
|
},
|
||||||
@ -132,9 +127,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redeemed": "redeemed",
|
"redeemed": "redeemed",
|
||||||
"remove": "Remove",
|
|
||||||
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
"removeNotSelf": "As an admin/moderator, you cannot delete yourself.",
|
||||||
"remove_all": "Remove all users",
|
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"statistic": {
|
"statistic": {
|
||||||
"activeUsers": "Active members",
|
"activeUsers": "Active members",
|
||||||
|
|||||||
18
admin/src/locales/index.test.js
Normal file
18
admin/src/locales/index.test.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import locales from './index.js'
|
||||||
|
|
||||||
|
describe('locales', () => {
|
||||||
|
it('should contain 2 locales', () => {
|
||||||
|
expect(locales).toHaveLength(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should contain a German locale', () => {
|
||||||
|
expect(locales).toContainEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: 'Deutsch',
|
||||||
|
code: 'de',
|
||||||
|
iso: 'de-DE',
|
||||||
|
enabled: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import ContributionLinks from './ContributionLinks.vue'
|
import ContributionLinks from './ContributionLinks.vue'
|
||||||
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
import { listContributionLinks } from '@/graphql/listContributionLinks.js'
|
||||||
|
import { toastErrorSpy } from '../../test/testSetup'
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
@ -46,13 +47,31 @@ describe('ContributionLinks', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
describe('apollo returns', () => {
|
||||||
|
it('calls listContributionLinks', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
query: listContributionLinks,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('calls listContributionLinks', () => {
|
describe.skip('query transaction with error', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
beforeEach(() => {
|
||||||
expect.objectContaining({
|
apolloQueryMock.mockRejectedValue({ message: 'OUCH!' })
|
||||||
query: listContributionLinks,
|
wrapper = Wrapper()
|
||||||
}),
|
})
|
||||||
)
|
|
||||||
|
it('calls the API', () => {
|
||||||
|
expect(apolloQueryMock).toBeCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('toast error', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith(
|
||||||
|
'listContributionLinks has no result, use default data',
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,337 +0,0 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
|
||||||
import Creation from './Creation.vue'
|
|
||||||
import { toastErrorSpy } from '../../test/testSetup'
|
|
||||||
|
|
||||||
const localVue = global.localVue
|
|
||||||
|
|
||||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
|
||||||
data: {
|
|
||||||
searchUsers: {
|
|
||||||
userCount: 2,
|
|
||||||
userList: [
|
|
||||||
{
|
|
||||||
userId: 1,
|
|
||||||
firstName: 'Bibi',
|
|
||||||
lastName: 'Bloxberg',
|
|
||||||
email: 'bibi@bloxberg.de',
|
|
||||||
creation: [200, 400, 600],
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const storeCommitMock = jest.fn()
|
|
||||||
|
|
||||||
const mocks = {
|
|
||||||
$t: jest.fn((t, options) => (options ? [t, options] : t)),
|
|
||||||
$d: jest.fn((d) => d),
|
|
||||||
$apollo: {
|
|
||||||
query: apolloQueryMock,
|
|
||||||
},
|
|
||||||
$store: {
|
|
||||||
commit: storeCommitMock,
|
|
||||||
state: {
|
|
||||||
userSelectedInMassCreation: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Creation', () => {
|
|
||||||
let wrapper
|
|
||||||
|
|
||||||
const Wrapper = () => {
|
|
||||||
return mount(Creation, { localVue, mocks })
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('mount', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has a DIV element with the class.creation', () => {
|
|
||||||
expect(wrapper.find('div.creation').exists()).toBeTruthy()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('apollo returns user array', () => {
|
|
||||||
it('calls the searchUser query', () => {
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has two rows in the left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has nwo rows in the right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has correct data in first row ', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi')
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'Bloxberg',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'200 | 400 | 600',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'bibi@bloxberg.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has correct data in second row ', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'Benjamin',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'Blümchen',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'800 | 600 | 400',
|
|
||||||
)
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('push item', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the correct user in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'bibi@bloxberg.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the correct user in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('updates userSelectedInMassCreation in store', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('remove item', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper
|
|
||||||
.findAll('table')
|
|
||||||
.at(1)
|
|
||||||
.findAll('tbody > tr')
|
|
||||||
.at(0)
|
|
||||||
.find('button')
|
|
||||||
.trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has two items in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the removed user in first row', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has no items in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits empty array as userSelectedInMassCreation', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('remove all bookmarks', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await wrapper.find('button.btn-light').trigger('click')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has no items in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('commits empty array to userSelectedInMassCreation', () => {
|
|
||||||
expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls searchUsers', () => {
|
|
||||||
expect(apolloQueryMock).toBeCalled()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('store has items in userSelectedInMassCreation', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
mocks.$store.state.userSelectedInMassCreation = [
|
|
||||||
{
|
|
||||||
userId: 2,
|
|
||||||
firstName: 'Benjamin',
|
|
||||||
lastName: 'Blümchen',
|
|
||||||
email: 'benjamin@bluemchen.de',
|
|
||||||
creation: [800, 600, 400],
|
|
||||||
showDetails: false,
|
|
||||||
emailChecked: true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in left table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has one item in right table', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('has the stored user in second row', () => {
|
|
||||||
expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain(
|
|
||||||
'benjamin@bluemchen.de',
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('failed creations', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper
|
|
||||||
.findComponent({ name: 'CreationFormular' })
|
|
||||||
.vm.$emit('toast-failed-creations', ['bibi@bloxberg.de', 'benjamin@bluemchen.de'])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toasts two error messages', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith([
|
|
||||||
'creation_form.creation_failed',
|
|
||||||
{ email: 'bibi@bloxberg.de' },
|
|
||||||
])
|
|
||||||
expect(toastErrorSpy).toBeCalledWith([
|
|
||||||
'creation_form.creation_failed',
|
|
||||||
{ email: 'benjamin@bluemchen.de' },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('watchers', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('search criteria', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await wrapper.setData({ criteria: 'XX' })
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls API when criteria changes', async () => {
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: 'XX',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('reset search criteria', () => {
|
|
||||||
it('calls the API', async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
await wrapper.find('.test-click-clear-criteria').trigger('click')
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls API when currentPage changes', async () => {
|
|
||||||
await wrapper.setData({ currentPage: 2 })
|
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
searchText: '',
|
|
||||||
currentPage: 2,
|
|
||||||
pageSize: 25,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('apollo returns error', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
apolloQueryMock.mockRejectedValue({
|
|
||||||
message: 'Ouch',
|
|
||||||
})
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toasts an error message', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith('Ouch')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,200 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="creation">
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="12" lg="6">
|
|
||||||
<label>{{ $t('user_search') }}</label>
|
|
||||||
<b-input-group>
|
|
||||||
<b-form-input
|
|
||||||
type="text"
|
|
||||||
class="test-input-criteria"
|
|
||||||
v-model="criteria"
|
|
||||||
:placeholder="$t('user_search')"
|
|
||||||
></b-form-input>
|
|
||||||
|
|
||||||
<b-input-group-append class="test-click-clear-criteria" @click="criteria = ''">
|
|
||||||
<b-input-group-text class="pointer">
|
|
||||||
<b-icon icon="x" />
|
|
||||||
</b-input-group-text>
|
|
||||||
</b-input-group-append>
|
|
||||||
</b-input-group>
|
|
||||||
<select-users-table
|
|
||||||
v-if="itemsList.length > 0"
|
|
||||||
:items="itemsList"
|
|
||||||
:fields="Searchfields"
|
|
||||||
@push-item="pushItem"
|
|
||||||
/>
|
|
||||||
<b-pagination
|
|
||||||
pills
|
|
||||||
v-model="currentPage"
|
|
||||||
per-page="perPage"
|
|
||||||
:total-rows="rows"
|
|
||||||
align="center"
|
|
||||||
:hide-ellipsis="true"
|
|
||||||
></b-pagination>
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="12" lg="6" class="shadow p-3 mb-5 rounded bg-info">
|
|
||||||
<div v-show="itemsMassCreation.length > 0">
|
|
||||||
<div class="text-right pr-4 mb-1">
|
|
||||||
<b-button @click="removeAllBookmarks()" variant="light">
|
|
||||||
<b-icon icon="x" scale="2" variant="danger"></b-icon>
|
|
||||||
|
|
||||||
{{ $t('remove_all') }}
|
|
||||||
</b-button>
|
|
||||||
</div>
|
|
||||||
<selected-users-table
|
|
||||||
class="shadow p-3 mb-5 bg-white rounded"
|
|
||||||
:items="itemsMassCreation"
|
|
||||||
:fields="fields"
|
|
||||||
@remove-item="removeItem"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div v-if="itemsMassCreation.length === 0">
|
|
||||||
{{ $t('multiple_creation_text') }}
|
|
||||||
</div>
|
|
||||||
<creation-formular
|
|
||||||
v-else
|
|
||||||
type="massCreation"
|
|
||||||
:creation="creation"
|
|
||||||
:items="itemsMassCreation"
|
|
||||||
@remove-all-bookmark="removeAllBookmarks"
|
|
||||||
@toast-failed-creations="toastFailedCreations"
|
|
||||||
/>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script>
|
|
||||||
import CreationFormular from '../components/CreationFormular.vue'
|
|
||||||
import SelectUsersTable from '../components/Tables/SelectUsersTable.vue'
|
|
||||||
import SelectedUsersTable from '../components/Tables/SelectedUsersTable.vue'
|
|
||||||
import { searchUsers } from '../graphql/searchUsers'
|
|
||||||
import { creationMonths } from '../mixins/creationMonths'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'Creation',
|
|
||||||
mixins: [creationMonths],
|
|
||||||
components: {
|
|
||||||
CreationFormular,
|
|
||||||
SelectUsersTable,
|
|
||||||
SelectedUsersTable,
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showArrays: false,
|
|
||||||
itemsList: [],
|
|
||||||
itemsMassCreation: this.$store.state.userSelectedInMassCreation,
|
|
||||||
radioSelectedMass: '',
|
|
||||||
criteria: '',
|
|
||||||
rows: 0,
|
|
||||||
currentPage: 1,
|
|
||||||
perPage: 25,
|
|
||||||
now: Date.now(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
await this.getUsers()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async getUsers() {
|
|
||||||
this.$apollo
|
|
||||||
.query({
|
|
||||||
query: searchUsers,
|
|
||||||
variables: {
|
|
||||||
searchText: this.criteria,
|
|
||||||
currentPage: this.currentPage,
|
|
||||||
pageSize: this.perPage,
|
|
||||||
filters: {
|
|
||||||
byActivated: true,
|
|
||||||
byDeleted: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fetchPolicy: 'network-only',
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
this.rows = result.data.searchUsers.userCount
|
|
||||||
this.itemsList = result.data.searchUsers.userList.map((user) => {
|
|
||||||
return {
|
|
||||||
...user,
|
|
||||||
showDetails: false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (this.itemsMassCreation.length !== 0) {
|
|
||||||
const selectedIndices = this.itemsMassCreation.map((item) => item.userId)
|
|
||||||
this.itemsList = this.itemsList.filter((item) => !selectedIndices.includes(item.userId))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toastError(error.message)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
pushItem(selectedItem) {
|
|
||||||
this.itemsMassCreation = [
|
|
||||||
this.itemsList.find((item) => selectedItem.userId === item.userId),
|
|
||||||
...this.itemsMassCreation,
|
|
||||||
]
|
|
||||||
this.itemsList = this.itemsList.filter((item) => selectedItem.userId !== item.userId)
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
|
||||||
},
|
|
||||||
removeItem(selectedItem) {
|
|
||||||
this.itemsList = [
|
|
||||||
this.itemsMassCreation.find((item) => selectedItem.userId === item.userId),
|
|
||||||
...this.itemsList,
|
|
||||||
]
|
|
||||||
this.itemsMassCreation = this.itemsMassCreation.filter(
|
|
||||||
(item) => selectedItem.userId !== item.userId,
|
|
||||||
)
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', this.itemsMassCreation)
|
|
||||||
},
|
|
||||||
removeAllBookmarks() {
|
|
||||||
this.itemsMassCreation = []
|
|
||||||
this.$store.commit('setUserSelectedInMassCreation', [])
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
toastFailedCreations(failedCreations) {
|
|
||||||
failedCreations.forEach((email) =>
|
|
||||||
this.toastError(this.$t('creation_form.creation_failed', { email })),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
Searchfields() {
|
|
||||||
return [
|
|
||||||
{ key: 'bookmark', label: 'bookmark' },
|
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
|
||||||
{
|
|
||||||
key: 'creation',
|
|
||||||
label: this.creationLabel,
|
|
||||||
formatter: (value, key, item) => {
|
|
||||||
return value.join(' | ')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
fields() {
|
|
||||||
return [
|
|
||||||
{ key: 'email', label: this.$t('e_mail') },
|
|
||||||
{ key: 'firstName', label: this.$t('firstname') },
|
|
||||||
{ key: 'lastName', label: this.$t('lastname') },
|
|
||||||
{
|
|
||||||
key: 'creation',
|
|
||||||
label: this.creationLabel,
|
|
||||||
formatter: (value, key, item) => {
|
|
||||||
return value.join(' | ')
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ key: 'bookmark', label: this.$t('remove') },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentPage() {
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
criteria() {
|
|
||||||
this.getUsers()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -45,7 +45,7 @@ describe('router', () => {
|
|||||||
|
|
||||||
describe('routes', () => {
|
describe('routes', () => {
|
||||||
it('has nine routes defined', () => {
|
it('has nine routes defined', () => {
|
||||||
expect(routes).toHaveLength(9)
|
expect(routes).toHaveLength(8)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has "/overview" as default', async () => {
|
it('has "/overview" as default', async () => {
|
||||||
@ -67,13 +67,6 @@ describe('router', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creation', () => {
|
|
||||||
it('loads the "Creation" component', async () => {
|
|
||||||
const component = await routes.find((r) => r.path === '/creation').component()
|
|
||||||
expect(component.default.name).toBe('Creation')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('creation-confirm', () => {
|
describe('creation-confirm', () => {
|
||||||
it('loads the "CreationConfirm" component', async () => {
|
it('loads the "CreationConfirm" component', async () => {
|
||||||
const component = await routes.find((r) => r.path === '/creation-confirm').component()
|
const component = await routes.find((r) => r.path === '/creation-confirm').component()
|
||||||
|
|||||||
@ -19,10 +19,6 @@ const routes = [
|
|||||||
path: '/user',
|
path: '/user',
|
||||||
component: () => import('@/pages/UserSearch.vue'),
|
component: () => import('@/pages/UserSearch.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/creation',
|
|
||||||
component: () => import('@/pages/Creation.vue'),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/creation-confirm',
|
path: '/creation-confirm',
|
||||||
component: () => import('@/pages/CreationConfirm.vue'),
|
component: () => import('@/pages/CreationConfirm.vue'),
|
||||||
|
|||||||
@ -15,7 +15,8 @@
|
|||||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
||||||
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||||
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
|
||||||
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts"
|
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
|
||||||
|
"locales": "scripts/sort.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hyperswarm/dht": "^6.2.0",
|
"@hyperswarm/dht": "^6.2.0",
|
||||||
|
|||||||
25
backend/scripts/sort.sh
Executable file
25
backend/scripts/sort.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
ROOT_DIR=$(dirname "$0")/..
|
||||||
|
|
||||||
|
tmp=$(mktemp)
|
||||||
|
exit_code=0
|
||||||
|
|
||||||
|
for locale_file in $ROOT_DIR/src/locales/*.json
|
||||||
|
do
|
||||||
|
jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp"
|
||||||
|
if [ "$*" == "--fix" ]
|
||||||
|
then
|
||||||
|
mv "$tmp" $locale_file
|
||||||
|
else
|
||||||
|
if diff -q "$tmp" $locale_file > /dev/null ;
|
||||||
|
then
|
||||||
|
: # all good
|
||||||
|
else
|
||||||
|
exit_code=$?
|
||||||
|
echo "$(basename -- $locale_file) is not sorted by keys"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit $exit_code
|
||||||
13
backend/scripts/sort_filter.jq
Normal file
13
backend/scripts/sort_filter.jq
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
def walk(f):
|
||||||
|
. as $in
|
||||||
|
| if type == "object" then
|
||||||
|
reduce keys_unsorted[] as $key
|
||||||
|
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||||
|
elif type == "array" then map( walk(f) ) | f
|
||||||
|
else f
|
||||||
|
end;
|
||||||
|
|
||||||
|
def keys_sort_by(f):
|
||||||
|
to_entries | sort_by(.key|f ) | from_entries;
|
||||||
|
|
||||||
|
walk(if type == "object" then keys_sort_by(ascii_upcase) else . end)
|
||||||
@ -16,6 +16,7 @@ import {
|
|||||||
redeemTransactionLink,
|
redeemTransactionLink,
|
||||||
createContribution,
|
createContribution,
|
||||||
updateContribution,
|
updateContribution,
|
||||||
|
createTransactionLink,
|
||||||
} from '@/seeds/graphql/mutations'
|
} from '@/seeds/graphql/mutations'
|
||||||
import { listTransactionLinksAdmin } from '@/seeds/graphql/queries'
|
import { listTransactionLinksAdmin } from '@/seeds/graphql/queries'
|
||||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||||
@ -24,6 +25,7 @@ import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||||
|
import { logger } from '@test/testSetup'
|
||||||
|
|
||||||
// mock semaphore to allow use fake timers
|
// mock semaphore to allow use fake timers
|
||||||
jest.mock('@/util/TRANSACTIONS_LOCK')
|
jest.mock('@/util/TRANSACTIONS_LOCK')
|
||||||
@ -50,7 +52,75 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('TransactionLinkResolver', () => {
|
describe('TransactionLinkResolver', () => {
|
||||||
|
describe('createTransactionLink', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await mutate({
|
||||||
|
mutation: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when amount is zero', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createTransactionLink,
|
||||||
|
variables: {
|
||||||
|
amount: 0,
|
||||||
|
memo: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [new GraphQLError('Amount must be a positive number')],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when amount is negative', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createTransactionLink,
|
||||||
|
variables: {
|
||||||
|
amount: -10,
|
||||||
|
memo: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [new GraphQLError('Amount must be a positive number')],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(-10))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws error when user has not enough GDD', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
await expect(
|
||||||
|
mutate({
|
||||||
|
mutation: createTransactionLink,
|
||||||
|
variables: {
|
||||||
|
amount: 1001,
|
||||||
|
memo: 'Test',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toMatchObject({
|
||||||
|
errors: [new GraphQLError('User has not enough GDD')],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('User has not enough GDD', expect.any(Number))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('redeemTransactionLink', () => {
|
describe('redeemTransactionLink', () => {
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
describe('contributionLink', () => {
|
describe('contributionLink', () => {
|
||||||
describe('input not valid', () => {
|
describe('input not valid', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -61,6 +131,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws error when link does not exists', async () => {
|
it('throws error when link does not exists', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
mutate({
|
mutate({
|
||||||
mutation: redeemTransactionLink,
|
mutation: redeemTransactionLink,
|
||||||
@ -69,16 +140,26 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: No contribution link found to given code: CL-123456',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'No contribution link found to given code',
|
||||||
|
'CL-123456',
|
||||||
|
)
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('No contribution link found to given code'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const validFrom = new Date(now.getFullYear() + 1, 0, 1)
|
||||||
|
|
||||||
it('throws error when link is not valid yet', async () => {
|
it('throws error when link is not valid yet', async () => {
|
||||||
const now = new Date()
|
jest.clearAllMocks()
|
||||||
const {
|
const {
|
||||||
data: { createContributionLink: contributionLink },
|
data: { createContributionLink: contributionLink },
|
||||||
} = await mutate({
|
} = await mutate({
|
||||||
@ -88,7 +169,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
name: 'Daily Contribution Link',
|
name: 'Daily Contribution Link',
|
||||||
memo: 'Thank you for contribute daily to the community',
|
memo: 'Thank you for contribute daily to the community',
|
||||||
cycle: 'DAILY',
|
cycle: 'DAILY',
|
||||||
validFrom: new Date(now.getFullYear() + 1, 0, 1).toISOString(),
|
validFrom: validFrom.toISOString(),
|
||||||
validTo: new Date(now.getFullYear() + 1, 11, 31, 23, 59, 59, 999).toISOString(),
|
validTo: new Date(now.getFullYear() + 1, 11, 31, 23, 59, 59, 999).toISOString(),
|
||||||
maxAmountPerMonth: new Decimal(200),
|
maxAmountPerMonth: new Decimal(200),
|
||||||
maxPerCycle: 1,
|
maxPerCycle: 1,
|
||||||
@ -102,16 +183,21 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: Contribution link not valid yet',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
await resetEntity(DbContributionLink)
|
await resetEntity(DbContributionLink)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('Contribution link is not valid yet', validFrom)
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('Contribution link is not valid yet'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it('throws error when contributionLink cycle is invalid', async () => {
|
it('throws error when contributionLink cycle is invalid', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
const {
|
const {
|
||||||
data: { createContributionLink: contributionLink },
|
data: { createContributionLink: contributionLink },
|
||||||
@ -136,17 +222,22 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: Contribution link has unknown cycle',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
await resetEntity(DbContributionLink)
|
await resetEntity(DbContributionLink)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('Contribution link has unknown cycle', 'INVALID')
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('Contribution link has unknown cycle'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const validTo = new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 0)
|
||||||
it('throws error when link is no longer valid', async () => {
|
it('throws error when link is no longer valid', async () => {
|
||||||
const now = new Date()
|
jest.clearAllMocks()
|
||||||
const {
|
const {
|
||||||
data: { createContributionLink: contributionLink },
|
data: { createContributionLink: contributionLink },
|
||||||
} = await mutate({
|
} = await mutate({
|
||||||
@ -157,7 +248,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
memo: 'Thank you for contribute daily to the community',
|
memo: 'Thank you for contribute daily to the community',
|
||||||
cycle: 'DAILY',
|
cycle: 'DAILY',
|
||||||
validFrom: new Date(now.getFullYear() - 1, 0, 1).toISOString(),
|
validFrom: new Date(now.getFullYear() - 1, 0, 1).toISOString(),
|
||||||
validTo: new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 999).toISOString(),
|
validTo: validTo.toISOString(),
|
||||||
maxAmountPerMonth: new Decimal(200),
|
maxAmountPerMonth: new Decimal(200),
|
||||||
maxPerCycle: 1,
|
maxPerCycle: 1,
|
||||||
},
|
},
|
||||||
@ -170,14 +261,18 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: Contribution link is no longer valid',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
await resetEntity(DbContributionLink)
|
await resetEntity(DbContributionLink)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith('Contribution link is no longer valid', validTo)
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('Contribution link is no longer valid'),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: have this test separated into a transactionLink and a contributionLink part
|
// TODO: have this test separated into a transactionLink and a contributionLink part
|
||||||
@ -250,6 +345,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('does not allow the user to redeem the contribution link', async () => {
|
it('does not allow the user to redeem the contribution link', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
mutate({
|
mutate({
|
||||||
mutation: redeemTransactionLink,
|
mutation: redeemTransactionLink,
|
||||||
@ -258,13 +354,18 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error(
|
||||||
|
'The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('user has no pending contributions that would not allow to redeem the link', () => {
|
describe('user has no pending contributions that would not allow to redeem the link', () => {
|
||||||
@ -301,6 +402,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
mutate({
|
mutate({
|
||||||
mutation: redeemTransactionLink,
|
mutation: redeemTransactionLink,
|
||||||
@ -309,14 +411,17 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: Contribution link already redeemed today',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('Contribution link already redeemed today'),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
describe('after one day', () => {
|
describe('after one day', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
jest.useFakeTimers()
|
jest.useFakeTimers()
|
||||||
@ -349,6 +454,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
it('does not allow the user to redeem the contribution link a second time on the same day', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
await expect(
|
await expect(
|
||||||
mutate({
|
mutate({
|
||||||
mutation: redeemTransactionLink,
|
mutation: redeemTransactionLink,
|
||||||
@ -357,33 +463,65 @@ describe('TransactionLinkResolver', () => {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toMatchObject({
|
).resolves.toMatchObject({
|
||||||
errors: [
|
errors: [new GraphQLError('Creation from contribution link was not successful')],
|
||||||
new GraphQLError(
|
|
||||||
'Creation from contribution link was not successful. Error: Contribution link already redeemed today',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('logs the error thrown', () => {
|
||||||
|
expect(logger.error).toBeCalledWith(
|
||||||
|
'Creation from contribution link was not successful',
|
||||||
|
new Error('Contribution link already redeemed today'),
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('transaction links list', () => {
|
describe('listTransactionLinksAdmin', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
userId: 1, // dummy, may be replaced
|
userId: 1, // dummy, may be replaced
|
||||||
filters: null,
|
filters: null,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 5,
|
pageSize: 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: there is a test not cleaning up after itself! Fix it!
|
afterAll(async () => {
|
||||||
beforeAll(async () => {
|
await cleanDB()
|
||||||
await cleanDB()
|
resetToken()
|
||||||
resetToken()
|
})
|
||||||
|
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('returns an error', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: listTransactionLinksAdmin,
|
||||||
|
variables,
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
|
describe('without admin rights', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
user = await userFactory(testEnv, bibiBloxberg)
|
||||||
|
await mutate({
|
||||||
|
mutation: login,
|
||||||
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
describe('unauthenticated', () => {
|
|
||||||
it('returns an error', async () => {
|
it('returns an error', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
query({
|
query({
|
||||||
@ -398,22 +536,40 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('authenticated', () => {
|
describe('with admin rights', () => {
|
||||||
describe('without admin rights', () => {
|
beforeAll(async () => {
|
||||||
beforeAll(async () => {
|
// admin 'peter@lustig.de' has to exists for 'creationFactory'
|
||||||
user = await userFactory(testEnv, bibiBloxberg)
|
await userFactory(testEnv, peterLustig)
|
||||||
await mutate({
|
|
||||||
mutation: login,
|
|
||||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(async () => {
|
user = await userFactory(testEnv, bibiBloxberg)
|
||||||
await cleanDB()
|
variables.userId = user.id
|
||||||
resetToken()
|
variables.pageSize = 25
|
||||||
})
|
// bibi needs GDDs
|
||||||
|
const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
await creationFactory(testEnv, bibisCreation!)
|
||||||
|
// bibis transaktion links
|
||||||
|
const bibisTransaktionLinks = transactionLinks.filter(
|
||||||
|
(transactionLink) => transactionLink.email === 'bibi@bloxberg.de',
|
||||||
|
)
|
||||||
|
for (let i = 0; i < bibisTransaktionLinks.length; i++) {
|
||||||
|
await transactionLinkFactory(testEnv, bibisTransaktionLinks[i])
|
||||||
|
}
|
||||||
|
|
||||||
it('returns an error', async () => {
|
// admin: only now log in
|
||||||
|
await mutate({
|
||||||
|
mutation: login,
|
||||||
|
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await cleanDB()
|
||||||
|
resetToken()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('without any filters', () => {
|
||||||
|
it('finds 6 open transaction links and no deleted or redeemed', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
query({
|
query({
|
||||||
query: listTransactionLinksAdmin,
|
query: listTransactionLinksAdmin,
|
||||||
@ -421,219 +577,169 @@ describe('TransactionLinkResolver', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('401 Unauthorized')],
|
data: {
|
||||||
|
listTransactionLinksAdmin: {
|
||||||
|
linkCount: 6,
|
||||||
|
linkList: expect.not.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Da habe ich mich wohl etwas übernommen.',
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('with admin rights', () => {
|
describe('all filters are null', () => {
|
||||||
beforeAll(async () => {
|
it('finds 6 open transaction links and no deleted or redeemed', async () => {
|
||||||
// admin 'peter@lustig.de' has to exists for 'creationFactory'
|
await expect(
|
||||||
await userFactory(testEnv, peterLustig)
|
query({
|
||||||
|
query: listTransactionLinksAdmin,
|
||||||
user = await userFactory(testEnv, bibiBloxberg)
|
variables: {
|
||||||
variables.userId = user.id
|
...variables,
|
||||||
variables.pageSize = 25
|
filters: {
|
||||||
// bibi needs GDDs
|
withDeleted: null,
|
||||||
const bibisCreation = creations.find(
|
withExpired: null,
|
||||||
(creation) => creation.email === 'bibi@bloxberg.de',
|
withRedeemed: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listTransactionLinksAdmin: {
|
||||||
|
linkCount: 6,
|
||||||
|
linkList: expect.not.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Da habe ich mich wohl etwas übernommen.',
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
})
|
||||||
await creationFactory(testEnv, bibisCreation!)
|
})
|
||||||
// bibis transaktion links
|
|
||||||
const bibisTransaktionLinks = transactionLinks.filter(
|
describe('filter with deleted', () => {
|
||||||
(transactionLink) => transactionLink.email === 'bibi@bloxberg.de',
|
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
|
||||||
|
await expect(
|
||||||
|
query({
|
||||||
|
query: listTransactionLinksAdmin,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
filters: {
|
||||||
|
withDeleted: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listTransactionLinksAdmin: {
|
||||||
|
linkCount: 7,
|
||||||
|
linkList: expect.arrayContaining([
|
||||||
|
expect.not.objectContaining({
|
||||||
|
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Da habe ich mich wohl etwas übernommen.',
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
for (let i = 0; i < bibisTransaktionLinks.length; i++) {
|
|
||||||
await transactionLinkFactory(testEnv, bibisTransaktionLinks[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
// admin: only now log in
|
|
||||||
await mutate({
|
|
||||||
mutation: login,
|
|
||||||
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
describe('filter by expired', () => {
|
||||||
await cleanDB()
|
it('finds 5 open transaction links, 1 expired, and no redeemed', async () => {
|
||||||
resetToken()
|
await expect(
|
||||||
|
query({
|
||||||
|
query: listTransactionLinksAdmin,
|
||||||
|
variables: {
|
||||||
|
...variables,
|
||||||
|
filters: {
|
||||||
|
withExpired: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
|
listTransactionLinksAdmin: {
|
||||||
|
linkCount: 7,
|
||||||
|
linkList: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
expect.not.objectContaining({
|
||||||
|
memo: 'Da habe ich mich wohl etwas übernommen.',
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('without any filters', () => {
|
// TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory
|
||||||
it('finds 6 open transaction links and no deleted or redeemed', async () => {
|
describe.skip('filter by redeemed', () => {
|
||||||
await expect(
|
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
|
||||||
query({
|
await expect(
|
||||||
query: listTransactionLinksAdmin,
|
query({
|
||||||
variables,
|
query: listTransactionLinksAdmin,
|
||||||
}),
|
variables: {
|
||||||
).resolves.toEqual(
|
...variables,
|
||||||
expect.objectContaining({
|
filters: {
|
||||||
data: {
|
withDeleted: null,
|
||||||
listTransactionLinksAdmin: {
|
withExpired: null,
|
||||||
linkCount: 6,
|
withRedeemed: true,
|
||||||
linkList: expect.not.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Da habe ich mich wohl etwas übernommen.',
|
|
||||||
deletedAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
)
|
}),
|
||||||
})
|
).resolves.toEqual(
|
||||||
})
|
expect.objectContaining({
|
||||||
|
data: {
|
||||||
describe('all filters are null', () => {
|
listTransactionLinksAdmin: {
|
||||||
it('finds 6 open transaction links and no deleted or redeemed', async () => {
|
linkCount: 6,
|
||||||
await expect(
|
linkList: expect.arrayContaining([
|
||||||
query({
|
expect.not.objectContaining({
|
||||||
query: listTransactionLinksAdmin,
|
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
||||||
variables: {
|
createdAt: expect.any(String),
|
||||||
...variables,
|
}),
|
||||||
filters: {
|
expect.objectContaining({
|
||||||
withDeleted: null,
|
memo: 'Yeah, eingelöst!',
|
||||||
withExpired: null,
|
redeemedAt: expect.any(String),
|
||||||
withRedeemed: null,
|
redeemedBy: expect.any(Number),
|
||||||
},
|
}),
|
||||||
|
expect.not.objectContaining({
|
||||||
|
memo: 'Da habe ich mich wohl etwas übernommen.',
|
||||||
|
deletedAt: expect.any(String),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
).resolves.toEqual(
|
}),
|
||||||
expect.objectContaining({
|
)
|
||||||
data: {
|
|
||||||
listTransactionLinksAdmin: {
|
|
||||||
linkCount: 6,
|
|
||||||
linkList: expect.not.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Da habe ich mich wohl etwas übernommen.',
|
|
||||||
deletedAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('filter with deleted', () => {
|
|
||||||
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
|
|
||||||
await expect(
|
|
||||||
query({
|
|
||||||
query: listTransactionLinksAdmin,
|
|
||||||
variables: {
|
|
||||||
...variables,
|
|
||||||
filters: {
|
|
||||||
withDeleted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
data: {
|
|
||||||
listTransactionLinksAdmin: {
|
|
||||||
linkCount: 7,
|
|
||||||
linkList: expect.arrayContaining([
|
|
||||||
expect.not.objectContaining({
|
|
||||||
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Da habe ich mich wohl etwas übernommen.',
|
|
||||||
deletedAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('filter by expired', () => {
|
|
||||||
it('finds 5 open transaction links, 1 expired, and no redeemed', async () => {
|
|
||||||
await expect(
|
|
||||||
query({
|
|
||||||
query: listTransactionLinksAdmin,
|
|
||||||
variables: {
|
|
||||||
...variables,
|
|
||||||
filters: {
|
|
||||||
withExpired: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
data: {
|
|
||||||
listTransactionLinksAdmin: {
|
|
||||||
linkCount: 7,
|
|
||||||
linkList: expect.arrayContaining([
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
expect.not.objectContaining({
|
|
||||||
memo: 'Da habe ich mich wohl etwas übernommen.',
|
|
||||||
deletedAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory
|
|
||||||
describe.skip('filter by redeemed', () => {
|
|
||||||
it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => {
|
|
||||||
await expect(
|
|
||||||
query({
|
|
||||||
query: listTransactionLinksAdmin,
|
|
||||||
variables: {
|
|
||||||
...variables,
|
|
||||||
filters: {
|
|
||||||
withDeleted: null,
|
|
||||||
withExpired: null,
|
|
||||||
withRedeemed: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.objectContaining({
|
|
||||||
data: {
|
|
||||||
listTransactionLinksAdmin: {
|
|
||||||
linkCount: 6,
|
|
||||||
linkList: expect.arrayContaining([
|
|
||||||
expect.not.objectContaining({
|
|
||||||
memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(',
|
|
||||||
createdAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
expect.objectContaining({
|
|
||||||
memo: 'Yeah, eingelöst!',
|
|
||||||
redeemedAt: expect.any(String),
|
|
||||||
redeemedBy: expect.any(Number),
|
|
||||||
}),
|
|
||||||
expect.not.objectContaining({
|
|
||||||
memo: 'Da habe ich mich wohl etwas übernommen.',
|
|
||||||
deletedAt: expect.any(String),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -32,6 +32,7 @@ import { getUserCreation, validateContribution } from './util/creations'
|
|||||||
import { executeTransaction } from './TransactionResolver'
|
import { executeTransaction } from './TransactionResolver'
|
||||||
import QueryLinkResult from '@union/QueryLinkResult'
|
import QueryLinkResult from '@union/QueryLinkResult'
|
||||||
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
import { getLastTransaction } from './util/getLastTransaction'
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
|
||||||
@ -65,12 +66,16 @@ export class TransactionLinkResolver {
|
|||||||
const createdDate = new Date()
|
const createdDate = new Date()
|
||||||
const validUntil = transactionLinkExpireDate(createdDate)
|
const validUntil = transactionLinkExpireDate(createdDate)
|
||||||
|
|
||||||
|
if (amount.lessThanOrEqualTo(0)) {
|
||||||
|
throw new LogError('Amount must be a positive number', amount)
|
||||||
|
}
|
||||||
|
|
||||||
const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay)
|
const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay)
|
||||||
|
|
||||||
// validate amount
|
// validate amount
|
||||||
const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate)
|
const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate)
|
||||||
if (!sendBalance) {
|
if (!sendBalance) {
|
||||||
throw new Error("user hasn't enough GDD or amount is < 0")
|
throw new LogError('User has not enough GDD', user.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionLink = DbTransactionLink.create()
|
const transactionLink = DbTransactionLink.create()
|
||||||
@ -186,24 +191,15 @@ export class TransactionLinkResolver {
|
|||||||
.where('contributionLink.code = :code', { code: code.replace('CL-', '') })
|
.where('contributionLink.code = :code', { code: code.replace('CL-', '') })
|
||||||
.getOne()
|
.getOne()
|
||||||
if (!contributionLink) {
|
if (!contributionLink) {
|
||||||
logger.error('no contribution link found to given code:', code)
|
throw new LogError('No contribution link found to given code', code)
|
||||||
throw new Error(`No contribution link found to given code: ${code}`)
|
|
||||||
}
|
}
|
||||||
logger.info('...contribution link found with id', contributionLink.id)
|
logger.info('...contribution link found with id', contributionLink.id)
|
||||||
if (new Date(contributionLink.validFrom).getTime() > now.getTime()) {
|
if (new Date(contributionLink.validFrom).getTime() > now.getTime()) {
|
||||||
logger.error(
|
throw new LogError('Contribution link is not valid yet', contributionLink.validFrom)
|
||||||
'contribution link is not valid yet. Valid from: ',
|
|
||||||
contributionLink.validFrom,
|
|
||||||
)
|
|
||||||
throw new Error('Contribution link not valid yet')
|
|
||||||
}
|
}
|
||||||
if (contributionLink.validTo) {
|
if (contributionLink.validTo) {
|
||||||
if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) {
|
if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) {
|
||||||
logger.error(
|
throw new LogError('Contribution link is no longer valid', contributionLink.validTo)
|
||||||
'contribution link is no longer valid. Valid to: ',
|
|
||||||
contributionLink.validTo,
|
|
||||||
)
|
|
||||||
throw new Error('Contribution link is no longer valid')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let alreadyRedeemed: DbContribution | undefined
|
let alreadyRedeemed: DbContribution | undefined
|
||||||
@ -219,11 +215,7 @@ export class TransactionLinkResolver {
|
|||||||
})
|
})
|
||||||
.getOne()
|
.getOne()
|
||||||
if (alreadyRedeemed) {
|
if (alreadyRedeemed) {
|
||||||
logger.error(
|
throw new LogError('Contribution link already redeemed', user.id)
|
||||||
'contribution link with rule ONCE already redeemed by user with id',
|
|
||||||
user.id,
|
|
||||||
)
|
|
||||||
throw new Error('Contribution link already redeemed')
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -248,17 +240,12 @@ export class TransactionLinkResolver {
|
|||||||
)
|
)
|
||||||
.getOne()
|
.getOne()
|
||||||
if (alreadyRedeemed) {
|
if (alreadyRedeemed) {
|
||||||
logger.error(
|
throw new LogError('Contribution link already redeemed today', user.id)
|
||||||
'contribution link with rule DAILY already redeemed by user with id',
|
|
||||||
user.id,
|
|
||||||
)
|
|
||||||
throw new Error('Contribution link already redeemed today')
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
logger.error('contribution link has unknown cycle', contributionLink.cycle)
|
throw new LogError('Contribution link has unknown cycle', contributionLink.cycle)
|
||||||
throw new Error('Contribution link has unknown cycle')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,8 +295,7 @@ export class TransactionLinkResolver {
|
|||||||
logger.info('creation from contribution link commited successfuly.')
|
logger.info('creation from contribution link commited successfuly.')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
logger.error(`Creation from contribution link was not successful: ${e}`)
|
throw new LogError('Creation from contribution link was not successful', e)
|
||||||
throw new Error(`Creation from contribution link was not successful. ${e}`)
|
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
"addedContributionMessage": {
|
|
||||||
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
|
||||||
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
|
||||||
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
|
||||||
},
|
|
||||||
"accountActivation": {
|
"accountActivation": {
|
||||||
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:",
|
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:",
|
||||||
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
||||||
@ -19,6 +14,11 @@
|
|||||||
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
||||||
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
||||||
},
|
},
|
||||||
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
||||||
|
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
||||||
|
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
|
},
|
||||||
"contributionConfirmed": {
|
"contributionConfirmed": {
|
||||||
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
||||||
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
"addedContributionMessage": {
|
|
||||||
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
|
||||||
"subject": "Gradido: Message about your common good contribution",
|
|
||||||
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
|
||||||
},
|
|
||||||
"accountActivation": {
|
"accountActivation": {
|
||||||
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:",
|
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:",
|
||||||
"emailRegistered": "Your email address has just been registered with Gradido.",
|
"emailRegistered": "Your email address has just been registered with Gradido.",
|
||||||
@ -19,6 +14,11 @@
|
|||||||
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
||||||
"subject": "Gradido: Try To Register Again With Your Email"
|
"subject": "Gradido: Try To Register Again With Your Email"
|
||||||
},
|
},
|
||||||
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
||||||
|
"subject": "Gradido: Message about your common good contribution",
|
||||||
|
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||||
|
},
|
||||||
"contributionConfirmed": {
|
"contributionConfirmed": {
|
||||||
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
||||||
"subject": "Gradido: Your contribution to the common good was confirmed"
|
"subject": "Gradido: Your contribution to the common good was confirmed"
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="decayinformation-startblock">
|
<div class="decayinformation-startblock">
|
||||||
|
<div class="my-4">
|
||||||
|
<div class="font-weight-bold pb-2">{{ $t('form.memo') }}</div>
|
||||||
|
<div>{{ memo }}</div>
|
||||||
|
</div>
|
||||||
<div class="mt-3 mb-3 text-center">
|
<div class="mt-3 mb-3 text-center">
|
||||||
<b>{{ $t('decay.before_startblock_transaction') }}</b>
|
<b>{{ $t('decay.before_startblock_transaction') }}</b>
|
||||||
</div>
|
</div>
|
||||||
@ -8,5 +12,11 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'DecayInformation-StartBlock',
|
name: 'DecayInformation-StartBlock',
|
||||||
|
props: {
|
||||||
|
memo: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="decay-information-box">
|
<div class="decay-information-box">
|
||||||
<decay-information-before-startblock v-if="decay.start === null" />
|
<decay-information-before-startblock v-if="decay.start === null" :memo="memo" />
|
||||||
<decay-information-decay-startblock
|
<decay-information-decay-startblock
|
||||||
v-else-if="isStartBlock"
|
v-else-if="isStartBlock"
|
||||||
:amount="amount"
|
:amount="amount"
|
||||||
|
|||||||
@ -179,6 +179,17 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
disabled() {
|
||||||
|
if (
|
||||||
|
this.form.email.length > 5 &&
|
||||||
|
parseInt(this.form.amount) <= parseInt(this.balance) &&
|
||||||
|
this.form.memo.length > 5 &&
|
||||||
|
this.form.memo.length <= 255
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
isBalanceDisabled() {
|
isBalanceDisabled() {
|
||||||
return this.balance <= 0 ? 'disabled' : false
|
return this.balance <= 0 ? 'disabled' : false
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user